In this paper, the content

  1. Constructor-based dependency injection
  2. Setter-based dependency injection

Constructor-based dependency injection

case

Define two simple bean classes, BeanOne and BeanTwo, with the former dependent on the latter.

package com.crab.spring.ioc.demo02;

public class BeanTwo {}Copy the code
package com.crab.spring.ioc.demo02;

/ * * *@author zfd
 * @versionV1.0 *@date2022/1/12 for * /
public class BeanOne {
    private int age;
    private String name;
    private BeanTwo beanTwo;
   
    /** * constructor, used for dependency injection, defines three dependencies *@param age
     * @param name
     * @param beanTwo
     */
    public BeanOne(int age, String name, BeanTwo beanTwo) {
        this.age = age;
        this.name = name;
        this.beanTwo = beanTwo;
    }

    @Override
    public String toString(a) {
        return "BeanOne{" +
                "age=" + age +
                ", name='" + name + '\' ' +
                ", beanTwo=" + beanTwo +
                '} '; }}Copy the code

Bean definition and dependency injection are implemented through XML configuration files


      
<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="bean2" class="com.crab.spring.ioc.demo02.BeanTwo"/>

    <bean id="bean1" class="com.crab.spring.ioc.demo02.BeanOne">
        <constructor-arg name="age" index="0" type="int" value="20"/>
        <constructor-arg name="name" index="1" type="java.lang.String" value="xxx"/>
        <constructor-arg name="beanTwo" index="2" type="com.crab.spring.ioc.demo02.BeanTwo" ref="bean2"/>
    </bean>
</beans>

Copy the code

Let’s verify the injection with a test class

package com.crab.spring.ioc.demo02;
/ * * *@author zfd
 * @versionV1.0 *@date2022/1/12 17:09 * /
public class demo02Test {

    @Test
    public void test_construct(a) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo02/spring1.xml");
        BeanOne bean1 = context.getBean("bean1", BeanOne.class);
        System.out.println(bean1);
        context.close();
    }
Copy the code

The output is as follows

BeanOne{age=20, name='xxx', beanTwo=com.crab.spring.ioc.demo02.BeanTwo@5204062d}
Copy the code

All three of the BeanOne dependencies are injected through the constructor, as expected, and are simple.

constructor-argBreak down

The tag constructor-arg supports the following list of elements.

Element names role
name Parameter names
index Parameter index, starting at 0
type The parameter types
value value
ref Bean reference

For example, the configuration in this case

<bean id="bean1" class="com.crab.spring.ioc.demo02.BeanOne">
    <constructor-arg name="age" index="0" type="int" value="20"/>
    <constructor-arg name="name" index="1" type="java.lang.String" value="xxx"/>
    <constructor-arg name="beanTwo" index="2" 		            		type="com.crab.spring.ioc.demo02.BeanTwo" ref="bean2"/>
</bean>
Copy the code

Note: Without causing any confusion, some of the above elements do not have to be configured. If the parameter position can be located when index is specified, then name can be left out, and if a dependent bean is referenced by ref, type can be omitted

<bean id="bean1" class="com.crab.spring.ioc.demo02.BeanOne">
    <constructor-arg index="0" type="int" value="20"/>
    <constructor-arg index="1" type="java.lang.String" value="xxx"/>
    <constructor-arg  index="2" ref="bean2"/>
</bean>
Copy the code

Matters needing attention

When using the name element in
to specify parameter names in a constructor, this is especially true if the method parameter names are retained after compilation. For example

// Method parameters before compilation
public BeanOne(int age, String name, BeanTwo beanTwo)
// Compiled method parameters
public BeanOne(int var1, String var2, BeanTwo var3)
Copy the code

Compiling method parameters to the form var1 invalidates a construct injection that specifies only name.

How to solve it? Two methods are provided

  1. Add the following plugin configuration to the POM file and keep the parameter name

        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <encoding>UTF8</encoding>
                        <compilerArgs>
                            <arg>-parameters</arg>
                        </compilerArgs>
                    </configuration>
                </plugin>
    
            </plugins>
        </build>
    Copy the code
  2. Explicitly name constructor parameters using the @ConstructorProperties JDK annotation

        /** * constructor, used for dependency injection, defines three dependencies *@param age
         * @param name
         * @param beanTwo
         */
        @ConstructorProperties({"age", "name", "beanTwo"}) // Explicitly declare the construction parameter name
        public BeanOne(int age, String name, BeanTwo beanTwo) {
            this.age = age;
            this.name = name;
            this.beanTwo = beanTwo;
        }
    Copy the code

Setter-based dependency injection

Setter-based DI is accomplished by the container calling Setter methods on the bean after invoking the parameterless constructor or parameterless static factory methods to instantiate the bean.

A simple class called BeanThree relies on BeanOne and BeanTwo and provides setters to set it.

package com.crab.spring.ioc.demo02;

/ * * *@author zfd
 * @versionV1.0 *@date2022/1/13 8:18 * /
public class BeanThree {
    private BeanTwo beanTwo;
    private BeanOne beanOne;

    public void setBeanTwo(BeanTwo beanTwo) {
        this.beanTwo = beanTwo;
    }

    public void setBeanOne(BeanOne beanOne) {
        this.beanOne = beanOne; }}Copy the code

The corresponding configuration file can set property references or property values through ref or name in the tag property


      
<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="bean2" class="com.crab.spring.ioc.demo02.BeanTwo"/>

    <! Constructor injection -->
    <bean id="bean1" class="com.crab.spring.ioc.demo02.BeanOne">
        <constructor-arg name="age" index="0" type="int" value="20"/>
        <constructor-arg name="name" index="1" type="java.lang.String" value="xxx"/>
        <constructor-arg name="beanTwo" index="2" type="com.crab.spring.ioc.demo02.BeanTwo" ref="bean2"/>
    </bean>

    <! - setter injection -- -- >
    <bean id="bean3" class="com.crab.spring.ioc.demo02.BeanThree">
        <! -- 1 ref element -->
        <property name="beanOne" ref="bean1"></property>
        <! -- ref ref -->
        <property name="beanTwo">
            <ref bean="bean2"></ref>
        </property>
         <property name="name" value="xxxx"/>
    </bean>
</beans>
Copy the code

Run the test class and the results, and you can see that dependency injection is successful

public class demo02Test {

    @Test
    public void test_construct(a) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo02/spring1.xml");
        BeanOne bean1 = context.getBean("bean1", BeanOne.class);
        System.out.println(bean1);

        System.out.println("Demo Setter injection"); BeanThree beanThree = context.getBean(BeanThree.class); System.out.println(beanThree); context.close(); }}Copy the code
BeanOne{age=20, name='xxx'BeanTwo = com), crab, spring. The ioc. Demo02. BeanTwo @ 68 demonstrate Setter injection BeanThree ceda24} {name ='xxxx', beanTwo=com.crab.spring.ioc.demo02.BeanTwo@68ceda24, beanOne=BeanOne{age=20, name='xxx', beanTwo=com.crab.spring.ioc.demo02.BeanTwo@68ceda24}}
Copy the code

Dependency resolution process

  • ApplicationContext is created and initialized with configuration metadata that describes all beans. Configuration metadata can be specified through XML, Java code, or annotations.
  • For each bean, its dependencies are represented as properties, constructor parameters, or static factory method parameters (if static factory methods are used instead of normal constructors). These dependencies are provided to the bean when it is actually created.
  • Each property or constructor parameter is an actual definition of the value to be set, or a reference to another bean in the container.
  • Each property or constructor parameter with a value is converted from its specified format to the actual type of the property or constructor parameter. By default, Spring can convert values provided in string format to all of the built-in types, such as int, Long, String, Boolean, and so on.

What are circular dependencies and how can they be handled?

If you use constructor injection primarily, you might create a loop dependency scenario that cannot be resolved.

For example, class A requires an instance of class B to be injected through the constructor, and class B requires an instance of class A to be injected through the constructor. If the class A and class B bean is configured to mutual injection, the Spring IoC container will detect the circular reference at runtime, and throw BeanCurrentlyInCreationException.

One possible solution is to edit the source code for some classes configured by setters rather than constructors. Alternatively, avoid constructor injection and just use setter injection. In other words, you can configure circular dependencies using setter injection.

Unlike the typical case, where there are no cyclic dependencies, A cyclic dependency between Beans A and B forces one bean to inject the other before it is fully initialized (A typical chicken-and-egg scenario).

Every man has his hobbyhorse

Constructor injection or Setter injection?

Spring’s official recommendation is that constructors are used to enforce dependencies and setter methods or configuration methods are used for optional dependencies. Note that the @required annotation on setter methods can be used to make a property a Required dependency; However, constructor injection with programmatic validation with parameters is preferable.

The Spring team generally advocates constructor injection because it allows you to implement application components as immutable objects and ensure that required dependencies are not null. In addition, constructor injected components are always returned to the client (calling) code in a fully initialized state. As a side note, the large number of constructor parameters is a bad code smell, meaning that the class may have too many responsibilities and should be refactored to better address the appropriate separation of concerns. Setter injection should primarily be used only for optional dependencies that can be assigned reasonable defaults in a class. Otherwise, non-null checks must be performed wherever the code uses dependencies. One benefit of setter injection is that setter methods enable objects of that class to be reconfigured or reinjected later.

Sometimes, when dealing with a third-party class without source code, constructor injection may be the only form of DI available if the third-party class does not expose any setter methods.

conclusion

This article demonstrates two methods of dependency injection: constructor injection and Setter method injection, and compares how to choose between them. The next article continues with dependency injection.

This source code address: github.com/kongxubihai…

Knowledge sharing, reproduced please indicate the source. Learning has no priority, the first!