Spring之依赖注入

本文目的

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的依赖注入有几种方式

从Spring的文档得知主要有两种:构造器和Setter方式

DI exists in two major variants: Constructor-based dependency injection and Setter-based dependency injection

构造器注入

官方文档,提到几个实例都是基于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();
	}


}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值