Spring Bean 依赖注入
依赖注入是 Spring 协调不同 Bean 实例之间的引用而提供的一种机制,当 Bean 实例A运行过程中需要引用另外一个 Bean 的实例B时,容器会创建实例B,并将实例B通过实例A的构造函数、setter方法等方式注入到实例A中,这个过程称为依赖注入。
依赖注入的好处就是隔离 Bean 之间的代码耦合,提高 Bean 的可重用性。Spring框架提供了构造函数注入、设置方法注入、自动装配注入和注解注入四种注入方式。
注入方法
使用构造器注入
通过配置构造器参数实现。除了构造器方式,还有静态工厂、实例工厂方法可以进行构造器注入,有如下 Java 类
public class HelloImpl implements IHelloService {
private String message;
private int index;
public HelloImpl(String message, int index) {
this.message = message;
this.index = index;
}
}
- 根据参数索引注入
<bean id="hello1" class="xxxx.HelloImpl">
<constructor-arg index="0" value="Hello"></constructor-arg>
<constructor-arg index="1" value="1"></constructor-arg>
</bean>
- 根据参数类型进行注入,会按顺序匹配
<bean id="hello2" class="xxxx.HelloImpl">
<constructor-arg type="java.lang.String" value="Hello"></constructor-arg>
<constructor-arg type="int" value="1"></constructor-arg>
</bean>
- 根据参数名进行注入
<bean id="hello3" class="xxxx.HelloImpl">
<constructor-arg name="message" value="Hello"></constructor-arg>
<constructor-arg name="index" value="2"></constructor-arg>
</bean>
- 还可以通过在构造器上添加 @java.beans.ConstructorProperties({“message”, “index”}) 注解来指定参数名字
Spring 类型转换系统对于 boolean 类型进行了容错处理,除了可以使用 “true/false” 标准的 Java 值进行注入,还能使用 “yes/no”、“on/off”、“1/0” 来代表。
Setter 方法注入
Setter 方法注入在,构造器注入后执行,对于上面的 Java 类进行如下配置,会覆盖构造器注入的属性值
<bean id="hello4" class="com.alex.practise.springhelloworld.HelloImpl">
<property name="message" value="Hello Ss"></property>
<property name="index" value="1"></property>
</bean>
Spring 如何知道 setter 方法?并将值注入?
方法名是要遵守约定的,setter 注入的方法名要遵循 “JavaBean getter/setter 方法命名约定”:
- 必须要有无参构造器
- 属性为 private 访问级别
- 属性可通过一组 setter 和 getter 方法来访问
setter方法,以“set” 开头,后跟首字母大写的属性名
getter方法,一般属性以“get”开头,对于 boolean 类型一般以“is”开头,后跟首字母大写的属性名
其他特殊情况,比如属性有连续两个大写字母开头,如“URL”,则 setter/getter 方法分别为:“setURL”和“getURL”
特殊类型注入
注入常量、集合、数组、字典、ID等
<property name="message" value="Hello Ss"></property>
<property name="values">
<list>
<value>1</value>
<value>2</value>
</list>
</property>
<property name="values">
<set>
<value>1</value>
<value>2</value>
</set>
</property>
注入依赖Bean及注入内部Bean
public class HelloDecorator implements IHelloService {
private IHelloService helloService;
// 无参构造器
public HelloDecorator() { }
// 有参构造器
public HelloDecorator(IHelloService helloService) {
this.helloService = helloService;
}
// setter 方法
public void setHelloService(IHelloService helloService) {
this.helloService = helloService;
}
@Override
public void sayHello() {
helloService.sayHello();
}
}
<bean id="hello" class="xxxx.HelloImpl"></bean>
<bean id="decorator1" class="xxxx.HelloDecorator">
<constructor-arg index="0" ref="hello"></constructor-arg>
</bean>
<bean id="decorator2" class="xxxx.HelloDecorator">
<property name="helloService">
<ref bean="hello" />
</property>
</bean>
内部 Bean
在
<bean id="bean0" class="xxxx.HelloDecorator">
<property name="helloService">
<bean id="innerBean" class="xxxx.HelloImpl"></bean>
</property>
</bean>
注入 NULL 值
通过
循环依赖问题
循环依赖就是循环引用,两个或多个 Bean 相互之间的持有对方,比如 CircleA 引用 CircleB,CircleB 引用 CircleC,CircleC 引用CircleA,最终反映为一个环。
注意:此处不是循环调用,循环调用是方法之间的环状调用, 循环调用是无法解决的,除非有终结条件,否则就是死循环。
Spring 如何解决循环依赖
如下代码所示,3个类互相依赖:
public class CircleA {
private CircleB circleB;
public CircleA() { }
public CircleA(CircleB circleB) {
this.circleB = circleB;
}
public void setCircleB(CircleB circleB) {
this.circleB = circleB;
}
public void a() {
circleB.b();
}
}
public class CircleB {
private CircleC circleC;
public CircleB() { }
public CircleB(CircleC circleC) {
this.circleC = circleC;
}
public void setCircleC(CircleC circleC) {
this.circleC = circleC;
}
public void b() {
circleC.c();
}
}
public class CircleC {
private CircleA circleA;
public CircleC() { }
public CircleC(CircleA circleA) {
this.circleA = circleA;
}
public void setCirclaC(CircleA circleA) {
this.circleA = circleA;
}
public void c() {
circleA.a();
}
}
构造器循环依赖
通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出 BeanCurrentlyInCreationException 异常表示循环依赖
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'circleA': Requested bean is currently in creation:
Is there an unresolvable circular reference?
Setter 方法循环依赖
对于 setter 注入造成的依赖是通过 Spring 容器提前暴露刚完成构造器注入但未完成其他步骤(如 setter 注入)的 Bean 来完成的,而且只能解决单例作用域的 Bean 循环依赖
通过暴露一个单例工厂方法,从而使其他 Bean 能引用到该 Bean
<bean id="circleA" class="xxxx.CircleA" >
<property name="circleB" ref="circleB"/>
</bean>
<bean id="circleB" class="xxxx.CircleB" >
<property name="circleC" ref="circleC"/>
</bean>
<bean id="circleC" class="xxxx.CircleC" >
<property name="circleA" ref="circleA"/>
</bean>
对于 prototype bean,Spring 容器无法完成依赖注入,因为 Spring 容器不缓存 prototype bean
解释:
Spring 内部所使用的两个内部属性:singletonFactories 和 earlySingletonObjects,这两个属性在 DefaultSingletonBeanRegistry 中被定义
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory> singletonFactories =
new HashMap<String, ObjectFactory>();
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects =
new HashMap<String, Object>();
singletonFactories,用于存储在 Spring 内部所使用的 beanName->对象工厂的引用,一旦最终对象被创建,此引用信息将删除。
earlySingletonObjects ,用于存储在创建 Bean 早期对创建的原始 Bean 的一个引用,注意这里是原始 Bean,即使用工厂方法或构造方法创建出来的对象,一旦对象最终创建好,此引用信息将删除。
Spring 源码
protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized(singletonObjects) {
if(!singletonObjects.containsKey(beanName)) {
singletonFactories.put(beanName, singletonFactory);
earlySingletonObjects.remove(beanName);
registeredSingletons.add(beanName);
}
}
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = singletonObjects.get(beanName);
if(singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized(singletonObjects) {
singletonObject = earlySingletonObjects.get(beanName);
if(singletonObject == null && allowEarlyReference) {
ObjectFactory singletonFactory = (ObjectFactory)singletonFactories.get(beanName);
if(singletonFactory != null) {
singletonObject = singletonFactory.getObject();
earlySingletonObjects.put(beanName, singletonObject);
singletonFactories.remove(beanName);
}
}
}
}
return singletonObject == NULL_OBJECT ? null : singletonObject;
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized(singletonObjects) {
singletonObjects.put(beanName, singletonObject == null ? NULL_OBJECT : singletonObject);
singletonFactories.remove(beanName);
earlySingletonObjects.remove(beanName);
registeredSingletons.add(beanName);
}
}