本文目的
1、探索Spring的依赖注入
2、Spring的依赖注入有几种方式
3、循环依赖,问题及解决
说明
前文介绍了@Autowired和@Resource的原理,觉得还是的写一下Spring的依赖注入,故写下此文。
探索Spring的依赖注入
依赖注入(Dependency injection (DI) ),是一个过程,在Spring中当创建bean时对依赖可以通过工厂方法或者实例构造器获取参数方法进行赋值,此过程就称之为依赖注入。它是来源另外一个概念的实现控制反转,故依赖注入也称之为控制反转。
控制反转( Inversion of Control (IoC)),它从传统的主动创建实例转为被动,比如我使用A的对象需要new A(),才能得到对象进行使用,而IoC则将此过程交给Spring容器,将创建实例的控制权交由Spring容器,只需要在使用时问Spring容器索取,不需要再有人为new后再使用,将创建实例控制权从主动转为代码(Spring容器),好比雇了个工人你提需求它提供结果。
Spring的依赖注入有几种方式
构造器注入
官方文档,提到几个实例都是基于XML,1、基于引用类型是根据类型进行依赖查找并注入
2、若是依赖的是基本数据类型,是无法通过类型进行注入的,a.通过指明基本数据类型以及默认值,b.假设有相同类型则可以通过参数位置索引和默认值(从左到右0开始)c.也可以为结合 @ConstructorProperties为参数配置名字,在xml配置中name注入默认值
构造函数参数引用类型
package x.y;
public class ThingOne {
public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
// ...
}
}
<beans>
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg ref="beanTwo"/>
<constructor-arg ref="beanThree"/>
</bean>
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
</beans>
package examples;
public class ExampleBean {
// Number of years to calculate the Ultimate Answer
private final int years;
// The Answer to Life, the Universe, and Everything
private final String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
构造函数参数类型匹配
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
构造函数参数索引
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
构造函数参数名称
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
package examples;
public class ExampleBean {
// Fields omitted
@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
Setter方式
Setter方式,可以通过@Require注解+RequiredAnnotationBeanPostProcessor,标记必须进行注入,需要通过修改AbstractBeanDefinition的AutowireModel(setter (1:byName/2:byType)方式进行注入,而 autowireModel=3为构造器注入,0:默认)需要改为1/2才能通过setter进行注入,但官方建议通过构造器进行依赖注入,setter只是在给外部提供修改依赖提供方便,而大部分情况下是不需要的
例子:
import org.springframework.stereotype.Component;
@Component
public class B1 {
public void say() {
System.out.println("I am " + this.getClass().getSimpleName());
}
}import org.springframework.beans.factory.annotation.Required;
import org.springframework.stereotype.Component;
@Component
public class A1 {
private B1 b1;
@Required
public void setB1(B1 b1) {
this.b1 = b1;
}
public B1 getB1() {
return b1;
}
}import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class DiTest {
/**
* @Required注解在5.1版本标记废除,但还是可以用的
* @deprecated as of 5.1, in favor of using constructor injection for required settings
* A1 生命B1的属性并声明setter方法并标记@Required,标记该方法在初始化后必须进行注入
* 但默认bean的注入方式是0,不会解析setter方法进行注入,
* 所以将对应bean的autowireModel改成byType=2(根据bean类型注入)或者byName=1(根据bean名称注入)
*/
@Test
public void testSetterRequireAnno(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(A1.class, B1.class);
//解析wired方法(setter)@Reqired baan后置处理器
applicationContext.register(RequiredAnnotationBeanPostProcessor.class);
//修改注入模式,setter byType
AbstractBeanDefinition a1Bd = (AbstractBeanDefinition)applicationContext.getBeanDefinition("a1");
a1Bd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
applicationContext.refresh();
//不加载RequiredAnnotationBeanPostProcessor或者不修改注入模型报NPE
//只加载RequiredAnnotationBeanPostProcessor,不修改注入模型创建a1实例提示必须注入b1实例
applicationContext.getBean(A1.class).getB1().say();
}
}
源码解析
提前找到setter方法
org.springframework.beans.ExtendedBeanInfo#handleCandidateWriteMethod
如果bean的注入模型是byType或者byName则从容器中找出对应依赖
RequiredAnnotationBeanPostProcessor,校验那些setter方法是@Required修饰且没有找到依赖值
/**
* 填充bean实例,属性值
* Populate the bean instance in the given BeanWrapper with the property values
* from the bean definition.
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @param bw the BeanWrapper with bean instance
*/
@SuppressWarnings("deprecation") // for postProcessPropertyValues
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// Skip property population phase for null instance.
return;
}
}
// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
return;
}
}
}
}
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
//倘若bean的注入模型是byName或者byType将各自的setter方法,并从容器中获取依赖,通过将属性名和对应依赖值存放在MutablePropertyValues中
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
//执行所有beanPostProcessor,包含属性扫描及注入
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
if (needsDepCheck) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
RequiredAnnotationBeanPostProcessor,属性值填充处理方法
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
if (!this.validatedBeanNames.contains(beanName)) {
if (!shouldSkip(this.beanFactory, beanName)) {
List<String> invalidProperties = new ArrayList<>();
for (PropertyDescriptor pd : pds) {
if (isRequiredProperty(pd) && !pvs.contains(pd.getName())) {
invalidProperties.add(pd.getName());
}
}
if (!invalidProperties.isEmpty()) {
throw new BeanInitializationException(buildExceptionMessage(invalidProperties, beanName));
}
}
this.validatedBeanNames.add(beanName);
}
return pvs;
}
循环依赖
主要使用构造函数注入,则可能会创建无法解决的循环依赖场景。
例如:A类通过构造函数注入需要B类的实例,B类通过构造函数注入需要A类的实例。如果为类 A 和 B 配置 bean 以相互注入,Spring IoC 容器会在运行时检测到这个循环引用,并抛出一个
BeanCurrentlyInCreationException
.
一种可能的解决方案是编辑某些类的源代码提供setter方法设置值而不是构造器设置值。也就是为了避免构造函数注入并仅使用 setter 注入。虽然不推荐,但是可以通过setter注入来配置循环依赖(注解注入也可以解决@Autowired,@Resource)。
bean A 和 bean B 之间的循环依赖关系强制其中一个 bean 在完全初始化之前注入另一个 bean(典型的先有鸡还是先有蛋的场景)。
示例
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
public class A {
public A(B b) {
}
}
import org.springframework.stereotype.Component;
@Component
public class B {
public B(A a) {
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class D {
@Autowired
private C c;
public void say(){
System.out.println("I am "+ this.getClass().getSimpleName() + ",field "+c);
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class C {
@Autowired
private D d;
public void say(){
System.out.println("I am "+ this.getClass().getSimpleName() + ",field "+d);
}
}
import org.springframework.stereotype.Component;
@Component
public class F {
private E e;
public void setE(E e) {
this.e = e;
}
public void say() {
System.out.println("I am " + this.getClass().getSimpleName() + ",field " + e);
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class E {
private F f;
public void setF(F f) {
this.f = f;
}
public void say(){
System.out.println("I am "+ this.getClass().getSimpleName() + ",field "+f);
}
}
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class CircularDependencyTest {
/**
* 循环依赖
* 构造器方式注入,异常,启动失败
* org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'a':
* Unsatisfied dependency expressed through constructor parameter 0;
* nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException:
* Error creating bean with name 'b': Unsatisfied dependency expressed through constructor parameter 0;
* nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:
* Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
* 因为实例化需要通过构造器,而注入又是通过构造器,A实例化需要B,而B实例化需要A,这时A还在等B实例化完成,从而进入死循环,Spring检测到快速失败
*
*/
@Test
public void testConstruct(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(A.class, B.class);
applicationContext.refresh();
}
/**
* 循环依赖
* setter方式注入(根据类型),正常启动,正常可以使用
*/
@Test
public void testSetterInject(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(E.class, F.class);
//修改注入模式,setter byType
AbstractBeanDefinition eBd = (AbstractBeanDefinition)applicationContext.getBeanDefinition("e");
eBd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
AbstractBeanDefinition fBd = (AbstractBeanDefinition)applicationContext.getBeanDefinition("f");
fBd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
applicationContext.refresh();
applicationContext.getBean(E.class).say();
applicationContext.getBean(F.class).say();
}
/**
* 循环依赖
* 注解方式注入,正常启动,正常可以使用
*/
@Test
public void testAnnoInject(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(C.class, D.class);
applicationContext.refresh();
applicationContext.getBean(C.class).say();
applicationContext.getBean(D.class).say();
}
}