This article has participated in the Denver Nuggets Creators Camp 3 “More Productive writing” track, see details: Digg project | creators Camp 3 ongoing, “write” personal impact.

Dependency injection

Dependency injection is when Spring creates a Bean, instantiating the parameters required by the Bean constructor or setting the Bean properties through Setter methods.

Spring has two types of constructor-based dependency injection and setter-based dependency injection.

Constructor-based dependency injection

Constructor injection is implemented through constructor parameters. As follows:

public class ExampleBean {

    // Number of years to calculate the Ultimate Answer
    private int years;

    // The Answer to Life, the Universe, and Everything
    private String ultimateAnswer;

    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer; }}Copy the code

The Bean has a two-parameter constructor, so how do you inject these parameters? There are three ways.

Method 1: Match by constructor type:

    <bean id="exampleBeanA" class="com.flydean.beans.ExampleBean">
        <constructor-arg type="int" value="7500000"/>
        <constructor-arg type="java.lang.String" value="42"/>
    </bean>
Copy the code

By specifying the parameter type, you can specify which parameter is years and which parameter is ultimateAnswer.

Method 2: Constructor index:

    <bean id="exampleBeanB" class="com.flydean.beans.ExampleBean">
        <constructor-arg index="0" value="7500000"/>
        <constructor-arg index="1" value="42"/>
    </bean>
Copy the code

In Spring, you can specify specific parameters through constructors’ indexes. Note that Spring’s index starts at 0.

Method 3: Constructor names match:

    <bean id="exampleBeanC" class="com.flydean.beans.ExampleBeanWithConstructorProperties">
        <constructor-arg name="years" value="7500000"/>
        <constructor-arg name="ultimateAnswer" value="42"/>
    </bean>
Copy the code

If the constructor name is used to match, it is important to note that the debug flag must be turned on at compile time, otherwise Spring will not find the parameter name in the constructor.

If you do not want to enable the debug flag, you must explicitly name the constructor parameters using the @ConstructorProperties JDK annotation.

public class ExampleBeanWithConstructorProperties {

    // Number of years to calculate the Ultimate Answer
    private int years;

    // The Answer to Life, the Universe, and Everything
    private String ultimateAnswer;

    @ConstructorProperties({"years", "ultimateAnswer"})
    public ExampleBeanWithConstructorProperties(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer; }}Copy the code

Setter-based injection

Setter injection is mainly used to set the properties of an object only after a no-argument constructor or an object instance has been obtained. Here is an example of a Setter:

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;

    // a setter method so that the Spring container can inject a MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}
Copy the code

The XML file is as follows:

    <! --Setter DI -->
    <bean id="movieFinder" class="com.flydean.beans.MovieFinder"/>

    <bean id="simpleMovieLister" class="com.flydean.beans.SimpleMovieLister">
        <property name="movieFinder" ref="movieFinder"/>
    </bean>
Copy the code

In addition to XML configuration, annotations can also be used: @Component, @Controller. Or use the @bean method in the @Configuration annotation.

How to choose?

Given these two methods of injection, how do we choose?

Typically, we inject necessary attributes through constructors. For optional properties, we inject through setters. Of course you can also use the @required annotation in Setter methods.

Of course, if a third-party class does not expose any setter methods, constructor injection may be the only form of DI available.

Circular dependencies

Loop dependencies occur primarily in the case of constructor injection.

Class A uses the constructor to inject instances that need class B, and class B uses the constructor to inject instances that need class A. If to each other into class A and class B configuration bean, then SpringIOC container detected at runtime the circular reference, throws BeanCurrentlyInCreationException.

The solution is to use Setter injection.

Dependency injection configuration details

Basic type, string or whatever

If the < property/> element’s value attribute is of a primitive type, Spring converts it to the type required by the class, as follows:

    <! --Setter DI -->
    <bean id="movieFinder" class="com.flydean.beans.MovieFinder">
        <property name="name" value="movie"/>
        <property name="number" value="123456"/>
    </bean>
Copy the code

This is a common Setter injection. For brevity, p-namespace can also be used, as follows:


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

    <bean id="movieFinder" class="com.flydean.beans.MovieFinder"
          p:name="movie"
          p:number="123456"/>
</beans>
Copy the code

The Spring container uses the JavaBeans property editor mechanism to convert the text in the < value/> element into an instance of java.util.properties. This is a nice shortcut, as follows:

    <bean id="mappings"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

        <! -- typed as a java.util.Properties -->
        <property name="properties">
            <value>
                jdbc.driver.className=com.mysql.jdbc.Driver
                jdbc.url=jdbc:mysql://localhost:3306/mydb
            </value>
        </property>
    </bean>
Copy the code

Notice the value inside the value in the above example.

ref

Bean attributes via the < ref/> tag allow you to create a reference to any bean in the same container or parent container, regardless of whether it is in the same XML file or not. The value of the bean property can be the same as the id property of the target bean or as a value in the name property of the target bean. The following example shows how to use the ref element:

<ref bean="someBean"/>
Copy the code

Inside the bean

A < bean/> element inside a < property/> or < constructor-arg/> element can define an internal bean. Here’s an example:

    <bean id="simpleMovieLister" class="com.flydean.beans.SimpleMovieLister">
        <property name="movieFinder">
            <bean class="com.flydean.beans.MovieFinder">
                <property name="name" value="movie"/>
                <property name="number" value="123456"/>
            </bean>
        </property>
    </bean>
Copy the code

The internal bean definition does not require an ID or name. If specified, the container will not use this value as an identifier. The container also ignores the scope flag when it is created, because internal beans are always anonymous and are always created with external beans. It is not possible to access internal beans individually, nor is it possible to inject them into collaborating beans other than closed beans.

A collection of

< list/>, < set/>, < map/>, and < props/> are used to set Properties and parameters of the Java Collection types list, set, map, and Properties, respectively. Here’s an example:

    <bean id="movieFinderE" class="com.flydean.beans.MovieFinder"></bean>

    <bean class="com.flydean.beans.CollectionBean">
        <property name="properties">
            <props>
                <prop key="administrator">[email protected]</prop>
                <prop key="support">[email protected]</prop>
                <prop key="development">[email protected]</prop>
            </props>
        </property>

        <property name="arrayList">
            <list>
                <value>"a list element followed by a reference"</value>
            </list>
        </property>

        <property name="hashMap">
            <map>
                <entry key="an entry" value="just some string"/>
                <entry key ="a ref" value-ref="movieFinderE"/>
            </map>
        </property>

        <property name="hashSet">
            <set>
                <value>just some string</value>
                <ref bean="movieFinderE" />
            </set>
        </property>
    </bean>
Copy the code

Strongly typed set

By introducing generic types in Java 5, you can use strongly typed collections. That is, you can declare a collection type that contains only (for example) string elements. If you use Spring to inject a strongly typed collection into a bean, you can take advantage of Spring’s type conversion support to convert elements of a strongly typed collection instance to the appropriate type before adding them to the collection. Here is an example of a Java class and bean definition:

public class SomeClass {

    private Map<String, Float> accounts;

    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts; }}Copy the code
        <bean id="something" class="com.flydean.beans.SomeClass">
            <property name="accounts">
                <map>
                    <entry key="one" value="9.99"/>
                    <entry key="two" value="2.75"/>
                    <entry key="six" value="3.99"/>
                </map>
            </property>
        </bean>
Copy the code

Null and Empty string values

Null and empty strings are different, as follows:

<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>
Copy the code

The above example is equivalent to:

exampleBean.setEmail("");
Copy the code

Here’s how to set null:

<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>
Copy the code

The above example is equivalent to:

exampleBean.setEmail(null);
Copy the code

c-namespace

The p-namespace is used to set the properties of the bean, and the c-namespace is used to set the constructor parameters, as shown below:

    <bean id="exampleBeanA" class="com.flydean.beans.ExampleBean">
        <constructor-arg type="int" value="7500000"/>
        <constructor-arg type="java.lang.String" value="42"/>
    </bean>

    <bean id="exampleBeanB" class="com.flydean.beans.ExampleBean"
    c:years ="10" c:ultimateAnswer="answer"
    />
Copy the code

depends-on

Typically, one bean depends on another bean, which we refer to in XML using < ref/>, but this dependency is not straightforward. We can use the depends-on attribute to explicitly force one or more beans to be initialized before beans that use this element are initialized, as follows:

    <bean id="beanA" class="com.flydean.beans.SomeClass" depends-on="beanB"></bean>

    <bean id="beanB" class="com.flydean.beans.MovieFinder"></bean>
Copy the code

lazy-init

Beans in the ApplicationContext that are configured to be singletons are normally initialized with Spring startup. If you need to extend the initialization of the bean, you can use lazy-init. A lazy-Initialized bean tells the IOC container to create the bean instance the first time it is requested, not at startup.

<bean id="beanD" class="com.flydean.beans.MovieFinder" lazy-init="true"></bean>
Copy the code

However, when a lazy-initialized bean is a dependency of a non-lazy-initialized Singleton bean, the ApplicationContext creates the lazy-initialized bean at startup because it must satisfy the Singleton dependency.

You can also control lazy initialization at the container level by using the default lazy init attribute on the < beans/> element, as shown in the following example:

<beans default-lazy-init="true">
    <! -- no beans will be pre-instantiated... -->
</beans>
Copy the code

Automatic loading

If you want Spring to automatically inject your bean’s dependent beans for you, use Spring’s Autowiring feature. Autowiring has four modes:

model instructions
no (Default) No auto load. The bean must refer to elements defined by ref. For larger deployments, changing the default Settings is not recommended, because explicitly specifying collaborators provides greater control and clarity. In a way, it records the structure of the system.
byName Automatically loads by property name. Spring looks for beans with the same name as the property you want to load automatically. For example, if the bean definition is set to auto-load by name and it contains a master property (that is, it has a setMaster (..)) Method), spring will look for the bean definition named Master and use it to set the property.
byType Automatic loading of properties is allowed if there is only one bean of attribute type in the container. If there are more than one, a fatal exception is thrown, which means that you cannot use byType auto-loading for the bean. If there is no matching bean, nothing happens (no properties are set).
constructor Similar to byType, but applicable to constructor arguments. If there is more than one constructor parameter type bean in the container, a fatal error is raised.

Limitations and limitations of automatic injection

While auto injection is fun to use, it also has the following drawbacks:

  • The display Settings for property and constructor-arg override automatic injection, and automatic injection cannot inject simple types.

  • Auto injection is not as accurate as display configuration.

  • Automatic injection may match many beans in the container. Problems can arise.

Exclude beans from auto-load

Setting the autowire-candidate property to false prevents the bean from being injected automatically. This property only affects how injection by type is performed. If you inject by name, it is not affected.

Here is an example of automatic injection:

    <bean id="simpleMovieLister" class="com.flydean.beans.SimpleMovieLister" autowire="byType">
    </bean>

    <! --Setter DI -->
    <bean id="movieFinder" class="com.flydean.beans.MovieFinder">
        <property name="name" value="movie"/>
        <property name="number" value="123456"/>
    </bean>
Copy the code

SimpleMovieLister is as follows:

package com.flydean.beans;

import lombok.Data;

@Data
public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;

    // a setter method so that the Spring container can inject a MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}
Copy the code

In the example above, the movieFinder property will be injected automatically.

Methods to inject

Problems can arise if one bean is dependent on another bean when the bean life cycle is different. For example, a singleton beanA depends on a multi-instance beanB. Because beanA is singleton, the dependent beanB is already created when beanA is created. It is not possible to create a new instance of beanB every time beanA needs a beanB.

There are many solutions, and we’ll go through them.

Approach 1: Implement ApplicationContextAware

If you implement ApplicationContextAware, you can request a new instance of beanB each time you need it through the getBean method, as follows:


public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand(a) {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext( ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext; }}Copy the code

This approach is undesirable because of the coupling between the business code and the Spring framework. Method injection is an advanced feature of the Spring IoC container that handles this situation well.

Find method injection

Lookup method injection is when the container overrides the method on the Container-managed bean and returns another named bean in the container. The lookup typically involves a prototype bean, as shown in the scenario described in the previous section. The Spring framework implements this method injection by dynamically generating subclasses that override the method using bytecode from the Cglib library.

Because cglib is used, beans cannot be final classes and methods cannot be final types.

The lookup method does not work for factory methods, especially the @Bean method in a configuration class, because in this case the container is not responsible for creating instances and therefore cannot dynamically create run-time generated subclasses.

Here is an example of a lookup method:

public abstract class CommandManagerB {

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command interface
        AsyncCommand command = createCommand();
        return null;
    }

    // okay... but where is the implementation of this method?
    public abstract AsyncCommand createCommand(a);
}
Copy the code

Here we define an abstract class, and the method to look for is createCommand. The object class returned is as follows:

public class AsyncCommand {}Copy the code

Here is the XML configuration file:

    <! -- a stateful bean deployed as a prototype (non-singleton) -->
    <bean id="myCommand" class="com.flydean.beans.AsyncCommand" scope="prototype">
        <! -- inject dependencies here as required -->
    </bean>

    <! -- commandProcessor uses statefulCommandHelper -->
    <bean id="commandManager" class="com.flydean.beans.CommandManagerB">
        <lookup-method name="createCommand" bean="myCommand"/>
    </bean>
Copy the code

CommandMangerB returns a new AsyncCommand instance each time createCommand is called.

In annotation-based cases, it can also be used like this:

public abstract class CommandManagerC {

    public Object process(Object commandState) {
        Command command = createCommand();
        return command.execute();
    }

    @Lookup("myCommand")
    protected abstract Command createCommand(a);
}
Copy the code

The name in @lookup (“myCommand”) can also be omitted, so the default type is used.

Arbitrary method substitution

We could even replace the bean’s method implementation with appet-method. We have a MyValueCalculator class and a method computeValue that we want to override.

public class MyValueCalculator {

    public String computeValue(String input) {
        // some real code...
        return "abc";
    }

    // some other methods...
}
Copy the code

An implementation of a org. Springframework. Beans. Factory. Support. MethodReplacer the class provides a new method of the interface, as shown below:

public class ReplacementComputeValue implements MethodReplacer {

    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
        // get the input value, work with it, and return a computed result
        String input = (String) args[0];
        return "def"; }}Copy the code

A bean is defined as follows:

    <bean id="myValueCalculator" class="com.flydean.beans.MyValueCalculator">
        <! -- arbitrary method replacement -->
        <replaced-method name="computeValue" replacer="replacementComputeValue">
            <arg-type>String</arg-type>
        </replaced-method>
    </bean>

    <bean id="replacementComputeValue" class="com.flydean.beans.ReplacementComputeValue"/>
Copy the code

Refer to the bean-di section of bean-di for examples in this chapter.

See flydean’s blog for more tutorials