In this paper, the content
- Constructor-based dependency injection
- 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-arg
Break 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
// 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
-
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
-
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!