Spring中到底有几种依赖注入的方式?4种
- 手动注入
- 自动注入
手动注入
- 在XML中定义Bean时,就是手动注入,因为是程序员手动给某个属性指定了值。
<bean name="userService" class="com.hhl.service.UserService">
<property name="orderService" ref="orderService"/>
</bean>
- 在XML中构造方法进行注入
<bean name="userService" class="com.hhl.service.UserService">
<constructor-arg index="0" ref="orderService"/>
</bean>
自动注入
- XML的autowire自动注入
- @Autowired注解的自动注入
XML的autowire自动注入,我们可以在定义一个Bean时去指定这个Bean的自动注入. 这么写,表示Spring会自动的给userService中所有的属性自动赋值(不需要这个属性上有@Autowired注解,但需要这个属性有对应的set方法)。
- byType
- byName
- constructor
- default
- no
<bean id="userService" class="com.hhl.service.UserService" autowire="byType"/>
创建Bean的过程中,在填充属性时,Spring会去解析当前类,把当前类的所有方法都解析出来。Spring会去解析每个方法得到对应的PropertyDescriptor对象,注意PropertyDescriptor并不是Spring中的类,而是java.beans包下类,也就是jdk自带的类,PropertyDescriptor中有几个属性:
- name:这个name并不是方法的名字,而是拿方法名字进过处理后的名字
- 如果方法名字以“get”开头,比如“getXXX”,那么name=XXX
- 如果方法名字以“is”开头,比如“isXXX”,那么name=XXX
- 如果方法名字以“set”开头,比如“setXXX”,那么name=XXX
- readMethodRef:表示get方法的Method对象的引用 3. readMethodName:表示get方法的名字 4. writeMethodRef:表示set方法的Method对象的引用 5. writeMethodName:表示set方法的名字 6. propertyTypeRef:如果有get方法那么对应的就是返回值的类型,如果是set方法那么对应的就是set方法中唯一参数的类型
Spring在通过byName的自动填充属性时流程是:
- 找到所有set方法所对应的XXX部分的名字
- 根据XXX部分的名字去获取bean
Spring在通过byType的自动填充属性时流程是:
- 找到所有set方法所对应的XXX部分的名字
- 根据XXX部分的名字重新再获取得到PropertyDescriptor
- 获取到set方法中的唯一参数的类型,并且根据该类型去容器中获取bean
- 如果找到多个,会报错
@Autowired注解相当于XML中的autowire属性的注解方式的替代&&更细粒度的控制。
- @Autowired注解可以写在:
- 属性上:先根据属性类型去找Bean,如果找到多个再根据属性名确定一个
- 构造方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个
- set方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个
@Autowired底层实现原理
- Spring会利用AutowiredAnnotationBeanPostProcessor的postProcessMergedBeanDefinition()找出注入点,把这些注入点信息找出来之后会进行缓存中(Set),InjectedElement就表示注入点.
- 进行注入:Spring会利用AutowiredAnnotationBeanPostProcessor的postProcessProperties()方法中,会遍历Set中的注入点开始进行注入。
关键方法 resolveDependency
@Nullable
Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException;
findAutowireCandidates()实现
- 找出BeanFactory中类型为type的所有的Bean的名字,注意是名字,而不是Bean对象,因为我们可以根据BeanDefinition就能判断和当前type是不是匹配
- 把resolvableDependencies中key为type的对象找出来并添加到result中
- 遍历根据type找出的beanName,判断当前beanName对应的Bean是不是能够被自动注入
- 先判断beanName对应的BeanDefinition中的autowireCandidate属性,如果为false,表示不能用来进行自动注入,如果为true则继续进行判断
- 判断当前type是不是泛型,如果是泛型是会把容器中所有的beanName找出来的,如果是这种情况,那么在这一步中就要获取到泛型的真正类型,然后进行匹配,如果当前beanName和当前泛型对应的真实类型匹配,那么则继续判断
- 如果当前DependencyDescriptor上存在@Qualifier注解,那么则要判断当前beanName上是否定义了Qualifier,并且是否和当前DependencyDescriptor上的Qualifier相等,相等则匹配
- 经过上述验证之后,当前beanName才能成为一个可注入的,添加到result中。
@Resource注解底层原理
- 如果@Resource注解中指定了name属性,那么则只会根据name属性的值去找bean,如果找不到则报错
- 如果@Resource注解没有指定name属性,那么会先判断当前注入点名字(属性名字或方法参数名字)是不是存在Bean,如果存在,则直接根据注入点名字取获取bean,如果不存在,则会走@Autowired注解的逻辑,会根据注入点类型去找Bean。
Spring中的Qualifier应用
@Qualifier可以用来明确指定想要指定哪个bean,它和@Autowired和@Resource的区别是什么? 如下bean定义:
<bean id="user0" class="com.hhl.entity.User">
<property name="name" value="user0000"/>
</bean>
<bean id="user1" class="com.hhl.entity.User" >
<property name="name" value="user1111"/>
</bean>
<bean name="userService" class="com.hhl.service.UserService" autowire="constructor"/>
public class UserService {
private User user;
public UserService(User user11) {
this.user = user11;
}
public void test() {
System.out.println(user.getName());
}
}
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.test();
}
启动Spring,会报错:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.luban.entity.User' available: expected single matching bean but found 2: user0,user1
根据构造方法参数类型User是找bean,找到了两个,一个user0, 一个user1,然后会根据构造方法参数名user11去进行过滤,但是过滤不出来,最终还是找到了两个,所以没法进行自动注入,所以报错。
解决方式
- bean设置primary为true,表示找到多个时默认用这个。
- 给某个bean设置autowire-candidate为false,表示这个bean不作为自动注入的候选者。
- 给两个bean分别定义一个qualifier:
<bean id="user0" class="com.hhl.entity.User" autowire-candidate="false">
<property name="name" value="user0000"/>
<qualifier value="user00"/>
</bean>
<bean id="user1" class="com.hhl.entity.User" >
<property name="name" value="user1111"/>
<qualifier value="user11"/>
</bean>
<context:annotation-config/>
public UserService(@Qualifier("user00") User user11) {
this.user = user11;
}
循环依赖
// A依赖了B
class A{
public B b;
}
// B依赖了A
class B{
public A a;
}
JAVA 对象之间相互依赖是很正常的事情。 Spring中有的场景Spring自动帮我们解决了,而有的场景则需要自己来解决。 spring只是解决了setter方式 - 单例(singleton)-- www.cnblogs.com/jajian/p/10… BeanDefinition中生成bean过程
- 首先根据class推断构造方法
- 根据推断出来的构造方法,反射,得到一个对象(暂时叫做原始对象)
- 填充原始对象中的属性(依赖注入)
- AOP.....
A类中存在一个B类的b属性,当A类生成了一个原始对象之后,就会去给b属性去赋值,此时就会根据b属性的类型和属性名去BeanFactory中去获取B类所对应的单例bean。
- 如果此时BeanFactory中存在B对应的Bean,那么直接拿来赋值给b属性
- 如果此时BeanFactory中不存在B对应的Bean,则需要生成一个B对应的Bean,然后赋值给b属性。 此时B类在BeanFactory中还没有生成对应的Bean,那么就需要去生成,就会经过B的Bean的生命周期。 如果B类中存在一个A类的a属性,就需要A类对应的Bean,但是,触发B类Bean的创建的条件是A类Bean在创建过程中的依赖注入,所以这里就出现了循环依赖:
- ABean创建-->依赖了B属性-->触发BBean创建--->B依赖了A属性--->需要ABean(但ABean还在创建过程中) Spring引入三级缓存 来解决(set方法和构造器的依赖注入都有解决不了的情况--编写的过程中要避免)该问题。
三级缓存
- 一级缓存为:singletonObjects->中缓存的是已经经历了完整生命周期的bean对象。
- 二级缓存为:earlySingletonObjects->(AOP)缓存的是早期的bean对象。早期是什么意思?表示Bean的生命周期还没走完就把这个Bean放入了earlySingletonObjects。
- 三级缓存为:singletonFactories->(AOP)中缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的。
假如我们自己解决这个循环依赖问题,会想到什么呢?软件程序实现无非CRUD,设计思想往往是加一层something->cache
- 把A的原始Bean放入缓存(提早暴露),整个过程中,都只有一个A原始对象,所以对于B而言,就算在属性注入时,注入的是A原始对象,也没有关系,因为A原始对象在后续的生命周期中在堆中没有发生变化。
只需要一个singletonObjects缓存就能解决循环依赖了,那么为什么Spring中还需要singletonFactories呢?
-
为了AOP-如果A的原始对象注入给B的属性之后,A的原始对象进行了AOP产生了一个代理对象.此时对于A而言,它的Bean对象其实应该是AOP之后的代理对象,而B的a属性对应的并不是AOP之后的代理对象,这就产生了冲突。
-
AOP就是通过一个BeanPostProcessor来实现的,这个BeanPostProcessor就是AnnotationAwareAspectJAutoProxyCreator,它的父类是AbstractAutoProxyCreator,而在Spring中AOP利用的要么是JDK动态代理,要么CGLib的动态代理,所以如果给一个类中的某个方法设置了切面,那么这个类最终就需要生成一个代理对象。
一般过程就是:A类--->生成一个普通对象-->属性注入-->基于切面生成一个代理对象-->把代理对象放入singletonObjects单例池中。
- 如何处理的,就是利用了第三级缓存singletonFactories。 singletonFactories中存的是某个beanName对应的ObjectFactory,在bean的生命周期中,生成完原始对象之后,就会构造一个ObjectFactory存入singletonFactories中。这个ObjectFactory是一个函数式接口,所以支持Lambda表达式:() -> getEarlyBeanReference(beanName, mbd, bean)
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);//AbstractAutoProxyCreator,InstantiationAwareBeanPostProcessorAdapter
}
}
}
return exposedObject;
}
// InstantiationAwareBeanPostProcessorAdapter
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
return bean;
}
// AbstractAutoProxyCreator
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
那么getEarlyBeanReference方法到底在干什么?
-
首先得到一个cachekey,cachekey就是beanName。
-
然后把beanName和bean(这是原始对象)存入earlyProxyReferences中
-
调用wrapIfNecessary进行AOP,得到一个代理对象。
-
这个ObjectFactory就是上文说的labmda表达式,中间有getEarlyBeanReference方法,注意存入singletonFactories时并不会执行lambda表达式,也就是不会执行getEarlyBeanReference方法.singletonFactories根据beanName得到一个ObjectFactory,然后执行ObjectFactory,也就是执行getEarlyBeanReference方法,此时会得到一个A原始对象经过AOP之后的代理对象,然后把该代理对象放入earlySingletonObjects中,注意此时并没有把代理对象放入singletonObjects中,那什么时候放入到singletonObjects中呢?
-
earlySingletonObjects的作用: 存放A原始对象的代理对象 我们只得到了A原始对象的代理对象,这个对象还不完整,因为A原始对象还没有进行属性填充,所以此时不能直接把A的代理对象放入singletonObjects中,所以只能把代理对象放入earlySingletonObjects,假设现在有其他对象依赖了A,那么则可以从earlySingletonObjects中得到A原始对象的代理对象了,并且是A的同一个代理对象。
-
当B创建完了之后,A继续进行生命周期,而A在完成属性注入后,会按照它本身的逻辑去进行AOP,而此时我们知道A原始对象已经经历过了AOP,所以对于A本身而言,不会再去进行AOP了,那么怎么判断一个对象是否经历过了AOP呢?
-
会利用上文提到的earlyProxyReferences,在AbstractAutoProxyCreator的postProcessAfterInitialization方法中,会去判断当前beanName是否在earlyProxyReferences,如果在则表示已经提前进行过AOP了,无需再次进行AOP。
-
对于A而言,进行了AOP的判断后,以及BeanPostProcessor的执行之后,就需要把A对应的对象放入singletonObjects中了,但是我们知道,应该是要A的代理对象放入singletonObjects中,所以此时需要从earlySingletonObjects中得到代理对象,然后入singletonObjects中。
总结
- singletonObjects:缓存某个beanName对应的经过了完整生命周期的bean
- earlySingletonObjects:缓存提前通过原始对象进行了AOP之后得到的代理对象,原始对象还没有进行属性注入和后续的BeanPostProcessor等生命周期
- singletonFactories:缓存的是一个ObjectFactory,也就是一个Lambda表达式。在创建一个Bean时,在每个Bean的生成过程中,都会提前暴露一个Lambda表达式,并保存到三级缓存中,这个Lambda表达式可能用到,也可能用不到,如果没有出现循环依赖依赖本bean,那么这个Lambda表达式无用,本bean按照自己的生命周期执行,执行完后直接把本bean放入singletonObjects中即可,如果出现了循环依赖依赖了本bean,则从三级缓存中获取Lambda表达式,并执行Lambda表达式得到一个AOP之后的代理对象(如果有AOP的话,如果无需AOP,则直接得到一个原始对象),并把得到的对象放入二级缓存earlyProxyReferences
- earlyProxyReferences:它用来记录某个原始对象是否进行过AOP了。