ProSpring5Chap3 - SpotBugsExtensionForSpringFrameWork/CS5098 GitHub Wiki

Introducing IoC and DI in Spring

Previously Chapter : dependency injection = IoC (interchangeable)

What this chapter about?

  • Inversion of control concepts
  • Inversion of control in Spring
    • DI services
      • Setter injection
      • Constructor injection
      • method injection
  • Dependency injection in Spring
    • Implementation of IoC container
      • BeanFactory
      • ApplicationContext
  • Configuring the Spring application context
    • XML configuration
    • Annotation configuration

1 Inversion of Control and Dependency Injection

Two subtypes of IoC

  • Dependency Lookup
  • Dependency Injection

2 Types of Inversion of Control

DL vs DI

  • Dependency Lookup (DL) : traditional / a component must acquire a reference to a dependency
  • Dependency Injection (DI) : more flexible and usable / dependencies are injected into the component by the IoC container

Two types of DL - No need to know

  • Dependency Pull : dependencies are pulled from a registry as required.
  • Contextualized Dependency Lookup (CDL) : similar to dependency pull, but in CDL, lookup is performed against the container that is managing the resource, not from some central registry, and it is usually performed at some set point.
public class ContextualizedDependencyLookup implements ManagedComponent {
    private Dependency dependency;
    @Override
    public void performLookup(Container container) {
        this.dependency = (Dependency) container.getDependency("myDependency");
    }
    @Override
    public String toString() {
        return dependency.toString();
    }
}
// By implementing this interface, a component is signaling to the container that it wants to obtain a dependency
public interface ManagedComponent {
    void performLookup(Container container);
}

Two common flavors of DI

  • constructor injection
    • The component declares a constructor or a set of constructors, taking as arguments its dependencies, and the IoC container passes the dependencies to the component when instantiation occurs
    • particularly useful when you must have an instance of the dependency class before your component is used.
  • setter dependency injection
    • A component’s setters expose the dependencies the IoC container can manage.
    • an object can be created without its dependencies, and they can be provided later by calling the setter.
public class ConstructorInjection {
    private Dependency dependency;
    public ConstructorInjection(Dependency dependency) {
        this.dependency = dependency;
    }
    @Override
    public String toString() {
        return dependency.toString();
    }
}

public class SetterInjection {
    private Dependency dependency;
    public void setDependency(Dependency dependency) {
        this.dependency = dependency;
    }
    @Override
    public String toString() {
        return dependency.toString();
    }
}

2.1 Injection vs. Lookup

Which one should use?

  • mandated by the container you are using
    • EJB 2.1 or prior versions - lookup
    • Spring - injection
  • injection: (adv)
    • zero impact on your components’ code
    • completely decoupled from the IoC container / easier testing
  • lookup: (disadv) always dependent on the classes and interfaces defined by the container (=tightly coupled) / difficult to test

Overall: write simple and less code using injection

2.2 Setter Injection vs. Constructor Injection

Constructutor injection:

  • must have an instance of the dependency class before your component is used
  • by using constructor injection, you assert the requirement for the dependency in a container-agnostic manner.
  • Constructor injection also helps achieve the use of immutable objects.

Setter injection:

  • the component is exposing its dependencies to the container but is happy to provide its own defaults
  • it allows dependencies to be declared on an interface
    • let each implementation class define its own dependencies and keep the business interface for business methods.

When to define dependecies in the business Interface

package com.apress.prospring5.ch3;

public interface NewsletterSender {


  void setSmtpServer(String smtpServer); 
  String getSmtpServer();
  void setFromAddress(String fromAddress); 
  String getFromAddress(); 
  
  void send();
}

The reason is that these values, the SMTP server address and the address the e-mails are sent from, are not dependencies in the practical sense; rather, they are configuration details that affect how all implementations of the NewsletterSender interface function.

what is the difference between a configuration parameter and any other kind of dependency?

See whether a dependency should be classified as a configuration parameter.

  • Configuration parameters are passive.
  • Configuration parameters are usually information, not other components.
  • Configuration parameters are usually simple values or collections of simple values.

Which one to choose?

Setter-based injection allows dependencies to be swapped out without creating new objects and also lets your class choose appropriate defaults without the need to explicitly inject an object.

Constructor injection is a good choice when you want to ensure that dependencies are being passed to a component and when designing for immutable objects.

3 Inversion of Control in Spring

Spring's dependency injection mechanism: In a Spring-based application, it is always preferable to use dependency injection to pass collaborators to dependent objects.

Refer to Figure 3-3.

Although dependency injection is the preferred mechanism for wiring together collaborators and dependent objects, you need dependency lookup to access the dependent objects. For example:

  • bootstrap Spring’s container in the main() method and obtain the dependencies (via the ApplicationContext interface)
  • But building web applications by using Spring’s MVC support, Spring can avoid this


4 Dependency Injection in Spring

  • Basics of Spring’s dependency injection container,
  • Looking at setter, constructor, and Method Injection,
  • How dependency injection is configured in Spring.

4.1 Beans and BeanFactory

BeanFactory Interface: Managing components

  • dependencies
  • life cycles

Bean is used to refer to any component managed by the container.

Application needs only DI support

  • must create an instance of a class that implements the BeanFactory interface
  • configures it with bean and dependency information

Application can access the beans via BeanFactory and get on with its processing

Configurations

Although the BeanFactory can be configured programmatically, it is more common to see it configured externally using some kind of configuration file.

Identification

Each bean can be assigned an ID, a name, or both.

4.2 BeanFactory Implementations

//interface package com.apress.prospring5.ch3;
public interface Oracle { String defineMeaningOfLife(); }

//implementation package com.apress.prospring5.ch3;
public class BookwormOracle implements Oracle { 
  private Encyclopedia encyclopedia;

	public void setEncyclopedia(Encyclopedia encyclopedia) { 
    this.encyclopedia = encyclopedia; 
  }

	@Override 
  public String defineMeaningOfLife() { 
    return "Encyclopedias are a waste of money - go see the world instead"; 
  }
}

// Application
package com.apress.prospring5.ch3;

import org.springframework.beans.factory.support.DefaultListableBeanFactory; 
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; 
import org.springframework.core.io.ClassPathResource;

public class XmlConfigWithBeanFactory {

	public static void main(String... args) { 
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); 
    XmlBeanDefinitionReader rdr = new XmlBeanDefinitionReader(factory); 
    rdr.loadBeanDefinitions(new ClassPathResource("spring/xml-bean-factory-config.xml")); 
    Oracle oracle = (Oracle) factory.getBean("oracle"); 
    System.out.println(oracle.defineMeaningOfLife()); 
  }
}

Using DefaultListableBeanFactory: one of the two main BeanFactory implementations supplied with Spring

Reading in the BeanDefinition information from an XML file by using XmlBeanDefinitionReader.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 									
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd">
  
  <bean id="oracle" 
        name="wiseworm"
				class="com.apress.prospring5.ch3.BookwormOracle"/> 
</beans>

The XML file declares a Spring bean, gives it an ID of oracle and a name of wiseworm, and tells Spring that the underlying implementation class is com.apress.prospring4.ch3.BookwormOracle.

In addition to XmlBeanDefinitionReader, Spring also provides PropertiesBeanDefinitionReader, which allows you to manage your bean configuration by using properties rather than XML.

4.3 ApplicationContext

In Spring, the ApplicationContext interface is an extension to BeanFactory.

In addition to DI services, ApplicationContext provides other services.

  • Transaction
  • AOP service
  • Message source for internationlization
  • application event handling
  • ...

In developing Spring-based applications, it’s recommended that you interact with Spring via the ApplicationContext interface.



5 Configuring ApplicationContext

Details of how to configure a Spring application, various aspects of configuring Spring application

ApplicationContext interface, which provides many more configuration options than the traditional BeanFactory interface.

5.1 Setting Spring Configuration Options

Types of configuration:

  • XML
  • Annotations
  • mix of both

Using an XML file can externalize all configuration from Java code, while annotations allow the developer to define and view the DI setup from within the code.

One common approach is:

  • Defining the application infrastructure (for example, data source, transaction manager, JMS connection factory, or JMX) in an XML file.
  • Defining the DI configuration (injectable beans and beans’ dependencies) in annotations.

5.2 Basic Configuration Overview

For XML configuration, you need to declare the required namespace base provided by Spring that your application requires.

XML-style configuration

app-context-xml.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:c="http://www.springframework.org/schema/c" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>

XML configuration with annotation support

app-context-annotation.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" 
       xmlns:c="http://www.springframework.org/schema/c" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd 
                           http://www.springframework.org/schema/context 
                           http://www.springframework.org/schema/context/spring-context.xsd">

<context:component-scan base-package="com.apress.prospring5.ch3.annotation"/>

</beans>

The <context:component-scan> tag tells Spring to scan the code for injectable beans annotated with @Component, @Controller, @Repository, and @Service as well as supporting the @Autowired, @Inject, and @Resource annotations under the package (and all its subpackages) specified.

Moreover, the tag supports inclusion and exclusion of a component scan for more fine-grained control.

<context:component-scan base-package="com.apress.prospring5.ch3.annotation">
  	<context:exclude-filter type="assignable" expression="com.example.NotAService"/> 
</context:component-scan>

The previous tag tells Spring to scan the package as specified but omit the classes that were assignable to the type as specified in the expression (can be either a class or an interface).

5.3 Declaring Spring Components

After you develop some kind of service class and want to use it in a Spring-based application, you need to tell Spring that those beans are eligible for injection to other beans and have Spring manage them for you.

Samples: the interfaces and the implementation of the two services

Setter injection
package com.apress.prospring5.ch2.decoupled;

//renderer interface 
public interface MessageRenderer {
  void render();
  void setMessageProvider(MessageProvider provider);
  MessageProvider getMessageProvider(); 
} 

// rendered implementation 
public class StandardOutMessageRenderer implements MessageRenderer {

  private MessageProvider messageProvider;
  
  @Override 
  public void render() {
    if (messageProvider == null) { 
      throw new RuntimeException( "You must set the property messageProvider of class:" + 
                                 StandardOutMessageRenderer.class.getName());
    }
    System.out.println(messageProvider.getMessage()); }

  @Override 
  public void setMessageProvider(MessageProvider provider) { this.messageProvider = provider; }

  @Override 
  public MessageProvider getMessageProvider() { return this.messageProvider; }
}

//provider interface 
public interface MessageProvider { String getMessage(); }

//provider implementation 
public class HelloWorldMessageProvider implements MessageProvider {
  
  @Override 
  public String getMessage() { return "Hello World!"; }

}
<bean id="provider" 
      class="com.apress.prospring5.ch2.decoupled.HelloWorldMessageProvider"/>

<bean id="renderer"
      class="com.apress.prospring5.ch2.decoupled.StandardOutMessageRenderer" 
      p:messageProvider-ref="provider"/>

The previous tags declare two beans, one with an ID of provider with the HelloWorldMessageProvider implementation, and the other with an ID of renderer with the StandardOutMessageRenderer implementation.

Create bean definitions using annotations

The bean classes must be annotated with the appropriate stereotype annotation,

and the methods or constructors must be annotated with @Autowired to tell the Spring IoC container to look for a bean of that type and use it as an argument when calling that method.

package com.apress.prospring5.ch3.annotation;

//simple bean 
@Component("provider") 
public class HelloWorldMessageProvider implements MessageProvider {
  ...
}

//complex, service bean 
@Service("renderer") 
public class StandardOutMessageRenderer implements MessageRenderer {
  ...
    
  @Override 
  public void render() {
  	...
  }
  
	@Override 
  @Autowired 
  public void setMessageProvider(MessageProvider provider) { 
    this.messageProvider = provider; 
  }
  
  @Override
  public MessageProcider getMessageProvider() {
    ...
  }

app-context-annotation.xml: Spring will seek out those components and instantiate the beans with the specified names:

<context:component-scan base-package="com.apress.prospring5.ch3.annotation"/>

Main method

Using either approach doesn’t affect the way you obtain the beans from ApplicationContext.

package com.apress.prospring5.ch3;

import com.apress.prospring5.ch2.decoupled.MessageRenderer; 
import org.springframework.context.support.GenericXmlApplicationContext;

public class DeclareSpringComponents {

  public static void main(String... args) {
    GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(); 
    ctx.load("classpath:spring/app-context-xml.xml"); 
    ctx.refresh(); 
    MessageRenderer messageRenderer = ctx.getBean("renderer", MessageRenderer.class); 
    messageRenderer.render(); ctx.close();
  }
}

Instead of DefaultListableBeanFactory, an instance of GenericXmlApplicationContext is instantiated. The GenericXmlApplicationContext class implements the ApplicationContext interface and is able to bootstrap Spring’s ApplicationContext from the configurations defined in XML files.

5.3.1 Using Java Configuration: xml -> Java configuration class

app-context-xml.xml can be replaced with a configuration class, without modifying the classes representing the bean types being created

A configuration class:

  • annotated with @Configuration
  • contains methods annotated with @Bean that are called directly by the Spring IoC container to instantiate the beans
    • The bean name will be the same as the name of the method used to create it.
@Configuration 
public class HelloWorldConfiguration {

  @Bean 
  public MessageProvider provider() { return new HelloWorldMessageProvider(); }

  @Bean 
  public MessageRenderer renderer(){
    MessageRenderer renderer = new StandardOutMessageRenderer();
    renderer.setMessageProvider(provider());
    return renderer; 
  }
}

A different implementation of ApplicationContext is needed.

public class HelloWorldSpringAnnotated {

  public static void main(String... args) { 
    ApplicationContext ctx = new AnnotationConfigApplicationContext (HelloWorldConfiguration.class);
    MessageRenderer mr = ctx.getBean("renderer", MessageRenderer.class);
    mr.render(); 
  }
}

Instead of DefaultListableBeanFactory, an instance of AnnotationConfigApplicationContext is instantiated.

More creative ways to define beans

@ComponentScan

A configuration class can be used to read the annotated beans definitions as well. In this case, because the bean’s definition configuration is part of the bean class, the class will no longer need any @Bean annotated methods.

package com.apress.prospring5.ch3.annotation;

@ComponentScan(basePackages = {"com.apress.prospring5.ch3.annotation"}) 
@Configuration 
public class HelloWorldConfiguration { 
}
@Component("provider")
public class HelloWorldMessageProvider implements MessageProvider {

	@Override
	public String getMessage() {
		return "Hello World!";
	}
}
@Service("renderer")
public class StandardOutMessageRenderer implements MessageRenderer {
    private MessageProvider messageProvider;

    @Override
    public void render() {
        if (messageProvider == null) {
            throw new RuntimeException(
            "You must set the property messageProvider of class:"
            + StandardOutMessageRenderer.class.getName());
        }

        System.out.println(messageProvider.getMessage());
    }

    @Override
    @Autowired
    public void setMessageProvider(MessageProvider provider) {
        this.messageProvider = provider;
    }

    @Override
    public MessageProvider getMessageProvider() {
        return this.messageProvider;
    }
}

This is done by annotating the configuration class with an annotation that is the equivalent of the <context:component-scanning ../> element. This annotation is @ComponentScanning and has the same parameters as the XML analogous element.

Mix of XML and Java Configuration
@ImportResource(locations = {"classpath:spring/app-context-xml.xml"}) 
@Configuration 
public class HelloWorldConfiguration {  
}

5.3.2 Using Setter Injection

XML configuration

To configure setter injection by using XML configuration, you need to specify <property> tags under the <bean> tag for each <property> into which you want to inject a dependency.

<beans ...>

  <bean id="renderer" class="com.apress.prospring5.ch2.decoupled.StandardOutMessageRenderer">
    <property name="messageProvider" ref="provider"/>
  </bean>
  
  <bean id="provider" class="com.apress.prospring5.ch2.decoupled.HelloWorldMessageProvider"/> 
</beans>

The provider bean is assigned to the messageProvider property. You can use the ref attribute to assign a bean reference to a property.

Spring 2.5 or newer and have the p namespace declared in your XML configuration file.

xmlns:p="http://www.springframework.org/schema/p"

p:messageProvider-ref="provider"

Annotation configuration

just need to add an @Autowired annotation to the setter method

@Service("renderer") 
public class StandardOutMessageRenderer implements MessageRenderer { 
  ...
  @Override 
  @Autowired   
  public void setMessageProvider(MessageProvider provider) { 
    this.messageProvider = provider; 
  } 
}

5.3.3 Using Constructor Injection

package com.apress.prospring5.ch3.xml;

import com.apress.prospring5.ch2.decoupled.MessageProvider;

public class ConfigurableMessageProvider implements MessageProvider { 
  private String message;

  public ConfigurableMessageProvider(String message) { 
    this.message = message; 
  }

  @Override 
  public String getMessage() { return message; }
}

it is impossible to create an instance of ConfigurableMessageProvider without providing a value for the message.

XML Configuration

The following code snippet shows how you can

  • redefine the provider bean definition to create an instance of ConfigurableMessageProvider,
  • injecting the message by using constructor injection:
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="messageProvider"
      class="com.apress.prospring5.ch3.xml.ConfigurableMessageProvider"> 
    	<constructor-arg value="I hope that someone gets my message in a bottle"/> 
  </bean> 
</beans>

In this code, instead of using a <property> tag, we used a <constructor-arg> tag.

There is only one good reason for using <constructor-arg>, and that is to create immutable objects.

Because we are not passing in another bean this time, just a String literal, we use the value attribute instead of ref to specify the value for the constructor argument.

If more than one constructor, give each <constructor-arg> tag an index attribute to specify the index of the argument, starting at 0, in the constructor signature.

you can also use the c namespace

xmlns:c="http://www.springframework.org/schema/c"

c:message="I hope that someone gets my message in a bottle"

Use Annotation for Constructor injection

we also use the @Autowired annotation in the target bean’s constructor method

@Autowired 
public ConfigurableMessageProvider(@Value("Configurable message") String message) { 
    this.message = message; 
}

@Value, to define the value to be injected into the constructor

Main method

package com.apress.prospring5.ch3;

import com.apress.prospring5.ch2.decoupled.MessageProvider; 
import org.springframework.context.support.GenericXmlApplicationContext;

public class DeclareSpringComponents {

  public static void main(String... args) { 
    GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(); 
    ctx.load("classpath:spring/app-context-annotation.xml"); 
    ctx.refresh();
    MessageProvider messageProvider = ctx.getBean("provider", MessageProvider.class);
    System.out.println(messageProvider.getMessage());
  }
}

Externalize values

A good practice is to externalize those values for injection.

<beans ...>
  <context:component-scan base-package="com.apress.prospring5.ch3.annotated"/>

  <bean id="message" class="java.lang.String"
        c:_0="I hope that someone gets my message in a bottle"/>
</beans>

Use the c namespace for constructor injection to set the string value, and _0 indicates the index for the constructor argument.

Having the bean declared, we can take away the @Value annotation from the target bean

@Autowired 
public ConfigurableMessageProvider(String message) { this.message = message; }

Multiple constructors

public ConstructorConfusion(String someValue) { 
  System.out.println("ConstructorConfusion(String) called"); 
  this.someValue = someValue; 
}

public ConstructorConfusion(int someValue) { 
  System.out.println("ConstructorConfusion(int) called"); 
  this.someValue = "Number: " + Integer.toString(someValue); 
}

If:

<constructor-arg> 
  <value>90</value>
</constructor-arg>

ConstructorConfusion(String) called.

<constructor-arg type="int"> 
  <value>90</value>
</constructor-arg>

Notice now that the <constructor-arg> tag has an additional attribute, type, that specifies the type of argument Spring should look for

5.3.4 Using Field Injection

There is a third type of dependency injection supported in Spring called field injection. As the name says, the dependency is injected directly in the field, with no constructor or setter needed.

Done by annotating the class member with the Autowired annotation

@Service("singer") 
public class Singer {

  @Autowired 
  private Inspiration inspirationBean;

  public void sing() { System.out.println("... " + inspirationBean.getLyric()); }
}

Main method

package com.apress.prospring5.ch3.annotated;

import org.springframework.context.support.GenericXmlApplicationContext;

public class FieldInjection {

  public static void main(String... args) {
    
    GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(); 
    ctx.load("classpath:spring/app-context.xml"); 
    ctx.refresh();

    Singer singerBean = ctx.getBean(Singer.class); 
    singerBean.sing();
    
    ctx.close();
  }
}

Drawbacks

  • be careful not to violate the single responsibility principle
  • Using field injections, it can become unclear what type of dependency is really needed and if the dependency is mandatory or not.
  • the bean is no longer a POJO and cannot be instantiated independently.
  • Field injection cannot be used for final fields.
  • Field injection introduces difficulties when writing tests

5.3.5 Injecting Simple Values

Using <value> tag:

Setter Injection
<bean id="injectSimpleSpel" class="com.apress.prospring5.ch3.xml.InjectSimpleSpel" p:name="John Mayer" p:age="39" p:height="1.92" p:programmer="false" p:ageInSeconds="1241401112"/>

define properties on your bean that accept String values, primitive values, or primitive wrapper values and then inject values for these properties by using the tag.

Annotation injection

Apply the @Value annotation to the bean properties.

@Service("injectSimple") 
public class InjectSimple {

  @Value("John Mayer") 
  private String name; 
  
  @Value("39") 
  private int age;
  
  ...
}

5.3.6 Injecting Values by Using SpEL

SpEL enables you to evaluate an expression dynamically and then use it in Spring’s ApplicationContext.

(Need to complete)

@Component vs. @Service

Basically, using @Component has the same effect as @Service. Both annotations are instructing Spring that the annotated class is a candidate for autodetection using annotation-based configuration and classpath scanning.

Practically, @Service is a specialization of @Component, which indicates that the annotated class is providing a business service to other layers within the application.

5.3.7 Injection and ApplicationContext Nesting

Spring supports a hierarchical structure for ApplicationContext so that one context (and hence the associating BeanFactory) is considered the parent of another.

Why hierarchical structure

By allowing ApplicationContexts to be nested, Spring allows you to split your configuration into different files, which is a godsend on larger projects with lots of beans.

How to

To nest one GenericXmlApplicationContext inside another, simply call the setParent() method in the child ApplicationContext.

public class HierarchicalAppContextUsage {

  public static void main(String... args) {

    GenericXmlApplicationContext parent = new GenericXmlApplicationContext(); 
    parent.load("classpath:spring/parent-context.xml"); 
    parent.refresh();

    GenericXmlApplicationContext child = new GenericXmlApplicationContext(); 
    child.load("classpath:spring/child-context.xml"); 
    child.setParent(parent);
    child.refresh();

    Song song1 = (Song) child.getBean("song1"); 
    Song song2 = (Song) child.getBean("song2"); 
    Song song3 = (Song) child.getBean("song3");

    System.out.println("from parent ctx: " + song1.getTitle()); 
    System.out.println("from child ctx: " + song2.getTitle()); 
    System.out.println("from parent ctx: " + song3.getTitle());

    child.close(); 
    parent.close();
  }

}

Inside the configuration file for the child ApplicationContext, referencing a bean in the parent ApplicationContext works exactly like referencing a bean in the child ApplicationContext.

parent-context.xml: this configuration simply defines two beans: childTitle and parentTitle.

<beans ...>
<bean id="childTitle" class="java.lang.String" c:_0="Daughters"/>
<bean id="parentTitle" class="java.lang.String" c:_0="Gravity"/> 
</beans>

child-context.xml:

<beans ...>

<bean id="song1" class="com.apress.prospring5.ch3.Song" 
      p:title-ref="parentTitle"/>

<bean id="song2" class="com.apress.prospring5.ch3.Song"
      p:title-ref="childTitle"/>

<bean id="song3" class="com.apress.prospring5.ch3.Song">
  <property name="title">
    <ref parent="childTitle"/> 
  </property>
</bean>

<bean id="childTitle" class="java.lang.String" c:_0="No Such Thing"/>
</beans>

We have defined four beans here. childTitle in this code is similar to childTitle in the parent except that the String it represents has a different value, indicating that it is located in the child ApplicationContext.

Explanation

The song1 bean is using the bean ref attribute to reference the bean named parentTitle. Because this bean exists only in the parent BeanFactory. There are two points of interest here.

  • First: use the bean attribute to reference beans in both the child and the parent ApplicationContexts.
  • ??? Second: you can’t use the local attribute to refer to beans in the parent ApplicationContext.

The song2 bean is using the bean ref attribute to reference childTitle. Because that bean is defined in both ApplicationContexts, the song2 bean receives a reference to childTitle in its own ApplicationContext.

The song3 bean is using the <ref> tag to reference childTitle directly in the parent ApplicationContext.

Result

As expected, the song1 and song3 beans both get a reference to beans in the parent ApplicationContext, whereas the song2 bean gets a reference to a bean in the child ApplicationContext.

from parent ctx: Gravity 
from child ctx: No Such Thing 
from parent ctx: Daughters

5.3.8 Injecting Collections

Using the collection is simple: you choose either <list>, <map>, <set>, or <props> to represent a List, Map, Set, or Properties instance, and then you pass in the individual items just as you would with any other injection.

public class CollectionInjection {


  private Map<String, Object> map; 
  private Properties props; 
  private Set set;
  private List list;
  
  // main method
  GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(); 
  ctx.load("classpath:spring/app-context-xml.xml"); 
  ctx.refresh();

  CollectionInjection instance = (CollectionInjection) ctx.getBean("injectCollection"); 
  instance.displayInfo();
  
  // set method
}

main() method retrieves a CollectionInjection bean from Spring and then calls the displayInfo() method.

<bean id="injectCollection" class="com.apress.prospring5.ch3.xml.CollectionInjection">

	<property name="map"> 
    <map> 
      <entry key="someValue">
        <value>It's a Friday, we finally made it</value> 
      </entry>
      <entry key="someBean">
        <ref bean="lyricHolder"/>
      </entry> 
    </map> 
  </property> 
  
  <property name="props"> 
    <props> 
      <prop key="firstName">John</prop>
      <prop key="secondName">Mayer</prop>
    </props> 
  </property>
  
  ...

injected values into all four setters exposed on the CollectionInjection class.


with the <list>, <map>, and <set> elements, you can employ any of the tags used to set the value of noncollection properties to specify the value of one of the entries in the collection.

Using this functionality, it is much easier to modularize your application and provide different, userselectable implementations of key pieces of application logic.

Using Spring’s collection injection, you can create a standard interface for this functionality, as shown:

public interface ArtworkSender { 
  void sendArtwork(String artworkPath, Recipient recipient); 
  String getFriendlyName(); 
  String getShortName(); 
}

From this interface, you can create multiple implementations, each of which is capable of describing itself to a human, such as the one shown here:

public class FtpArtworkSender implements ArtworkSender {

  @Override
  public void sendArtwork(String artworkPath, Recipient recipient) { 
    // ftp logic here...
  }
  @Override
  public String getFriendlyName() { return "File Transfer Protocol"; }
  @Override 
  public String getShortName() { return "ftp"; }
}

Imagine that you then develop an ArtworkManager class that supports all available implementations of the Artwork-Sender interface. With the implementations in place, you simply pass a List to your ArtworkManager class, and you are on your way. Using the getFriendlyName() method, you can display a list of delivery options for the system administrator to choose from when you are configuring each stationery template.

public class ArtworkManager {
  private List<ArtworkSender> senders;

  public ArtworkManager(List<ArtworkSender> senders) {
    this.senders = senders;
  }

  public void displaySenders() {
    System.out.println("All Senders:\n");

    for (ArtworkSender sender : senders) {
      System.out.println("Sender: " + sender.getFriendlyName());
    }
  }

  public static void main(String[] args) {
    GenericXmlApplicationContext context = new GenericXmlApplicationContext();
    context.load("classpath:sender.xml");
    context.refresh();

    ArtworkManager manager = (ArtworkManager) context.getBean("artworkManager");
    manager.displaySenders();
  }
}

5.3.9 Using Method Injection

  • Lookup Method Injection
  • Method Replacement

Lookup Method Injection provides another mechanism by which a bean can obtain one of its dependencies.

Method replacement allows you to replace the implementation of any method on a bean arbitrarily, without having to change the original source code.

5.3.9.1 Lookup Method Injection

Lookup Method Injection was added to Spring in version 1.1 to overcome the problems encountered when a bean depends on another bean with a different life cycle, specifically, when a singleton depends on a nonsingleton.

Sample:

LockOpener class - relies on a KeyHelper class.

KeyHelper class is not suitable for reuse. Every time the openLock() method is called, a new KeyHelper instance is required.

Therefore, LockOpener will be a singleton.

To make sure that a new instance of the KeyHelper instance is passed into the openLock() method every time it is invoked, we need to use Lookup Method Injection.

you can achieve this by having the singleton bean implement the ApplicationContextAware interface (we discuss this interface in the next chapter). Then, using the ApplicationContext instance, the singleton bean can look up a new instance of the nonsingleton dependency every time it needs it.

public interface DemoBean {
  Singer getMySinger();
  void doSomething(); 
}

The doSomething() method is a simple method that depends on the Singer class to do its processing.

public class StandardLookupDemoBean implements DemoBean {

	private Singer mySinger;

	public void setMySinger(Singer mySinger) {
		this.mySinger = mySinger;
	}

	@Override
	public Singer getMySinger() {
		return this.mySinger;
	}

	@Override
	public void doSomething() {
		mySinger.sing();
	}
}

but notice that the doSomething() method uses the stored instance of Singer to complete its processing.

Method Injection to obtain an instance of the Singer class.

public abstract class AbstractLookupDemoBean implements DemoBean {
    public abstract Singer getMySinger();

    @Override
    public void doSomething() {
        getMySinger().sing();
    }
}
<bean id="abstractLookupBean" class="com.apress.prospring5.ch3.AbstractLookupDemoBean">
  <lookup-method name="getMySinger" bean="singer"/> 
</bean>

For abstract-LookupBean, you need to configure the lookup method by using the <lookup-method> tag. The name attribute of the <lookup-method> tag tells Spring the name of the method on the bean that it should override.

DemoBean abstractBean = ctx.getBean("abstractLookupBean", DemoBean.class); 
DemoBean standardBean = ctx.getBean("standardLookupBean", DemoBean.class);

displayInfo("abstractLookupBean", abstractBean); displayInfo("standardLookupBean", standardBean);

Compared to two methods, Clearly, standardLookupBean should be faster because it returns the same instance each time.

[abstractLookupBean]: Singer Instances the Same? false 100000 gets took 431 ms

[standardLookupBean]: Singer Instances the Same? true 100000 gets took 1 ms

As you can see, the Singer instances are, as expected, the same when we use standardLookupBean and different when we use abstractLookupBean

5.3.9.2 Considerations for Lookup Method Injection

Lookup Method Injection is intended for use when you want to work with two beans of different life cycles. Avoid the temptation to use Lookup Method Injection when the beans share the same life cycle, especially if they are singletons.

When we define a bean with the singleton scope, the container creates a single instance of that bean; all requests for that bean name will return the same object, which is cached. Any modifications to the object will be reflected in all references to the bean. This scope is the default value if no other scope is specified.

5.3.9.3 Method Replacement

Using method replacement, you can replace the implementation of any method on any beans arbitrarily without having to change the source of the bean you are modifying.

For example, change a third-party library.

Internally, you achieve this by creating a subclass of the bean class dynamically. You use CGLIB and redirect calls to the method you want to replace to another bean that implements the MethodReplacer interface.

public class ReplacementTarget {
    public String formatMessage(String msg) {
        return "<h1>" + msg + "</h1>"; 
    }

    public String formatMessage(Object msg) {
        return "<h1>" + msg + "</h1>"; 
    }
}

You can replace any of the methods on the ReplacementTarget class by using Spring’s method replacement functionality.

Replacing formatMessage(String) method:

first need to create an implementation of the MethodReplacer interface

public class FormatMessageReplacer implements MethodReplacer {

	@Override
	public Object reimplement(Object arg0, Method method, Object[] args)
			throws Throwable {
		if (isFormatMessageMethod(method)) {
			String msg = (String) args[0];
			return "<h2>" + msg + "</h2>";
		} else {
			throw new IllegalArgumentException("Unable to reimplement method "
					+ method.getName());
		}
	}

	private boolean isFormatMessageMethod(Method method) {
		if (method.getParameterTypes().length != 1) {
			return false;
		}
		if (!("formatMessage".equals(method.getName()))) {
			return false;
		}
		if (method.getReturnType() != String.class) {
			return false;
		}
		if (method.getParameterTypes()[0] != String.class) {
			return false;
		}
		return true;
	}
}

5.3.9.4 When to Use Method Replacement

Method replacement can prove quite useful in a variety of circumstances, especially when you want to override only a particular method for a single bean rather than all beans of the same type.

5.4 Understanding Bean Naming

Every bean must have at least one name that is unique within the containing ApplicationContext.

id attribute -> as the name

If not, Spring looks for name attribute.

If not, Spring uses class name.

5.4.1 Bean Nme Alisaing

  • name attribute

Spring allows a bean to have more than one name. You can achieve this by specifying a space-, comma-, or semicolon-separated list of names in the name attribute of the bean’s <bean> tag.

  • alias attribute
<beans ...>

<bean id="john" name="john johnny,jonathan;jim" class="java.lang.String"/>

<alias name="john" alias="ion"/>

</beans>
Importance of name alisaing

Consider the following scenario: you have an application in which 50 beans, configured using Spring, all require an implementation of the Foo interface. Twenty-five of the beans use the StandardFoo implementation with the bean name standardFoo, and the other 25 use the SuperFoo implementation with the superFoo bean name. Six months after you put the application into production, you decide to move the first 25 beans to the SuperFoo implementation.

To do this, you have three options.

The second option is to update the injection configuration for the 25 beans that are changing, which changes the beans’ names from standardFoo to superFoo. This approach is not the most elegant way to proceed. You could perform a find and replace, but then rolling back your changes when management isn’t happy means retrieving an old version of your configuration from your version control system.

The third, and most ideal, approach is to remove (or comment out) the definition for the standardFoo bean and make standardFoo an alias to superFoo. This change requires minimal effort, and restoring the system to its previous configuration is just as simple.

5.4.2 Bean Naming with Annotation Configurations

(Can't understand so many annotations)

5.5 Understanding Bean Instantiation Mode

By default, all beans in Spring are singletons. This means Spring maintains a single instance of the bean, all dependent objects use the same instance, and all calls to ApplicationContext.getBean() return the same instance.

  • Singleton -> Nonsingleton (Prototype)

although changing the instantiation mode of your bean won’t affect your application code, it does cause some problems if you rely on Spring’s life-cycle interfaces. We cover this in more detail in Chapter 4.

@Component("nonSingleton")
@Scope("prototype")
public class Singer {
      private String name = "unknown";

      public Singer(@Value("John Mayer") String name) {
            this.name = name;
      }

      @Override public String toString() {
            return  name;
      }
}

@Scope : The prototype scope instructs Spring to instantiate a new instance of the bean every time a bean instance is requested by the application.

5.5.1 Choosing an Instantiation Mode

In general, singletons should be used in the following scenarios:

  • Shared object with no state
  • Shared object with read-only state
  • Shared object with shared state
  • High-throughput objects with writable state

Using nonsingletons in the following scenarios:

  • Objects with writable state
  • Objects with private state

5.5.2 Implementing Bean Scopes

  • Singleton
  • Prototype
  • Request: web application.
  • Session: web application
  • Global session: portlet-based web applications
  • Thread: A new bean instance will be created by Spring when requested by a new thread.
  • Custom

6 Resolving Dependencies

During normal operation, Spring is able to resolve dependencies by simply looking at your configuration file or annotations in your classes.

Unfortunately, Spring is not aware of any dependencies that exist between beans in your code that are not specified in the configuration. Spring maybe unaware to inject the dependencies, but simply get an instance of the class.

Configuration

<beans ...>

  <bean id="johnMayer" class="com.apress.prospring5.ch3.xml.Singer" depends-on="gopher"/> 
  <bean id="gopher" class="com.apress.prospring5.ch3.xml.Guitar"/>

</beans>

depends-on: Spring should take this into consideration when instantiating the beans and ensure that gopher is created before johnMayer.

To do this, though, johnMayer needs to access ApplicationContext. Thus, we also have to tell Spring to inject this reference, so when the johnMayer.sing() method will be called, it can be used to procure the gopher bean.

This is done by making the Singer bean implement the ApplicationContextAware interface.

@Component("johnMayer")
@DependsOn("gopher")
public class Singer implements ApplicationContextAware{

	ApplicationContext applicationContext;

	@Override 
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	private Guitar guitar;

	public Singer(){
	}

	public void sing() {
		guitar =  applicationContext.getBean("gopher", Guitar.class);
		guitar.sing();
	}
}

@DependsOn annotation

7 Autowiring Your Bean

Autowiring feature of spring framework enables you to inject the object dependency implicitly. It internally uses setter or constructor injection.

Advantage of Autowiring

It requires the less code because we don't need to write the code to inject the dependency explicitly.

Five modes of autowiring:

  • byName: The byName mode injects the object dependency according to name of the bean. In such case, property name and bean name must be same. It internally calls setter method.
  • byType: The byType mode injects the object dependency according to type. So property name and bean name can be different. It internally calls setter method.
  • constructor: This functions just like byType wiring, except that it uses constructors rather than setters to perform the injection. Spring attempts to match the greatest numbers of arguments it can in the constructor.
  • no: default

byName

  • property name and the bean name must be the same

  • setter method

Actually, the property name and the bean name do not need to be the same, but the setter method's name which defines the property needs to be exactly the same as the bean's name, except the 'set' word. For example,

<bean id="barOne" .../> and public void setBarOne(Bar bar) needs to be the same. 除了set和第一个大写字母。

byType

  • The byType mode injects the object dependency according to type. So property name and bean name can be different.
  • It internally calls setter method.

It works fine, but if more than one bean of the same type is available in the container, the framework will throw a fatal exception.

To resolve this conflict, we need to tell Spring explicitly which bean we want to inject.

  • First is to use primary attribute. But it is a solution only when there are just two bean-related types.
  • Second is use @Qualifier

When to Use Autowiring

In some cases, autowiring can save you time, but it does not really take that much extra effort to define your wiring explicitly, and you benefit from explicit semantics and full flexibility on property naming and on how many instances of the same type you manage.

In all, suggest no.

8 Setting Bean Inheritance

In some cases, you may need multiple definitions of beans that are the same type or implement a shared interface.

Spring allows you to provide a <bean> definition that inherits its property settings from another bean in the same ApplicationContext instance.

    <bean id="parent" class="com.apress.prospring5.ch3.xml.Singer"
        p:name="John Mayer" p:age="39" />
    
    <bean id="child" class="com.apress.prospring5.ch3.xml.Singer"
        parent="parent" p:age="0"/>

main method

	Singer parent = (Singer) ctx.getBean("parent"); 
	Singer child = (Singer) ctx.getBean("child");

Result:

Parent:
Name: John Mayer Age: 39

Child:
Name: John Mayer Age: 0

As expected, the inheritChild bean inherited the value for its name property from the inheritParent bean but was able to provide its own value for the age property.

⚠️ **GitHub.com Fallback** ⚠️