Spring源码解读---依赖注入源码解析
Posted *King*
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring源码解读---依赖注入源码解析相关的知识,希望对你有一定的参考价值。
Spring中依赖注入的方式
分为两种:手动注入、自动注入
手动注入
在XML中定义Bean时,就是手动注入,手动注入分为两种:set方法注入、构造方法注入
下面这种是通过set方法进行注入
<bean name="userService" class="com.example.spring.UserService">
<property name="orderService" ref="orderService"/>
</bean>
下面这种是通过构造方法进行注入
<bean name="userService" class="com.example.spring.UserService">
<constructor‐arg index="0" ref="orderService"/>
</bean>
自动注入
自动注入也分为两种:XML的autowire自动注入、@Autowired注解的自动注入
XML的autowire自动注入
在XML中,我们可以在定义一个Bean时去指定这个Bean的自动注入模式:
autowire的参数有:byType、byName、constructor、default、no
<bean name="userService" class="com.example.spring.UserService" autowire="byType"/>
这样写Spring会自动给UserService中所有的属性自动赋值,不需要在这个属性上加@Autowired注解,但需要这个属性有对应的set方法。
在创建Bean的过程中填充属性时Spring会去解析当前类,把当前类的所有方法都解析出来,Spring会去解析每个方法得到对应的PropertyDescriptor对象。
PropertyDescriptor中的属性:
- name:这个name并不是方法名字,而是方法名字进行处理后的名字,如setXXX,那么name=xXX
- readMethodRef:表示get方法的Method对象的引用
- readMethodName:表示get方法的名字
- writeMethodRef:表示set方法的Method对象的引用
- writeMethodName:表示set方法的名字
- propertyTypeRef:如果有get方法那么对应的就是返回值类型,如果是set方法那么对应的就是set方法中唯一参数的类型。
autowire的byType和byName情况:
Spring通过ByName自动填充属性时流程:
- 找到所有set方法所对应的XXX部分的名字
- 根据XXX部分的名字去获取bean
Spring通过ByType自动填充属性时流程:
- 获取到set方法中的唯一参数的参数类型,并且根据该类型去容器中获取bean
- 如果找到多个会报错
@Autowired注解的自动注入
@Autowired注解相当于XML中的autowire属性的注解方式的替代。@Autowired注解提供了与autowire相同的功能,但是拥有更细粒度的控 制和更广泛的适用性。
@Autowired无法区分byType和byName,@Autowired是先byType,如果找到多个则ByName。
@Autowired注解可以写在属性上,构造方法上,set方法上:
- 写在属性上:先根据属性类型去找Bean,如果找到多个再根据属性名确定一个
- 构造方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个
- set方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个
寻找注入点
在创建一个Bean的过程中,Spring会利用AutowiredAnnotationBeanPostProcessor的postProcessMergedBeanDefinition()找出注入点并缓存,找注入点的流程为:
- 遍历当前类的所有属性字段Field
- 查看字段上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该字段是一个注入点
- 如果字段是static的,则不进行注入
- 获取@Autowired中的required属性的值
- 将字段信息构造成一个AutowiredFieldElement对象,作为一个注入点对象添加到currElements集合中
- 遍历当前类的所有方法Method
- 判断当前Method是否是桥接方法,如果是找到原方法
- 查看方法上是否存在@Autowired、@Value、@Inject中其中任意一个,存在则认为该方法是一个注入点
- 如果方法是static的,则不进行注入
- 获取@Autowired中的required属性的值
- 将方法信息构造成一个AutowiredMethodElement对象,作为一个注入点对象添加到currElements集合中
- 遍历完当前类的字段和方法后,将遍历父类的,直到没有父类
- 最后将currElements集合封装成一个InjectionMetadata对象,作为当前Bean对象的注入点集合对象,并缓存
注入点进行注入
Spring在AutowiredAnnotationBeanPostProcessor的postProcessProperties方法中会遍历所找到的注入点依次进行注入
字段注入
- 遍历所有的AutowiredFieldElement对象
- 将对应的字段封装为DependencyDescriptor对象
- 调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依赖查找,找到当前字段所匹配的Bean对象
- 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,如果当前Bean是原型Bean,那么下次再来创建Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去拿bean对象了,不用再次进行查找
- 利用反射将结果对象赋值给字段
Set方法注入
- 遍历所有的AutowiredMethodElement对象
- 遍历将对应方法的参数,将每个参数封装成MethodParameter对象
- MethodParameter对象封装为DependencyDescriptor对象
- 调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依赖查找,找到当前方法参数所匹配的Bean对象
- 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次 再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去那bean对象了,不用再次进行查找了
- 利用反射将找到的所有结果对象传给当前方法,并执行
关于依赖注入中泛型注入的实现
在Java反射中,有一个Type接口,表示类型,具体分类为:
- raw types:普通Class
- parameterized types:对应ParameterizedType接口,泛型类型
- array types:对应GenericArrayType,泛型数组
- type variables:对应TypeVariable接口,表示类型变量,也就是所定义的泛型,比如T、K
- primitive types:基本类型,int、boolean
public class TypeTest
private int i;
private Integer it;
private int[] iarray;
private List list;
private List<String> slist;
private List<T> tlist;
private T t;
private T[] tarray;
public TypeTest()
public static void main(String[] args) throws NoSuchFieldException
test(TypeTest.class.getDeclaredField("i"));
System.out.println("=======");
test(TypeTest.class.getDeclaredField("it"));
System.out.println("=======");
test(TypeTest.class.getDeclaredField("iarray"));
System.out.println("=======");
test(TypeTest.class.getDeclaredField("list"));
System.out.println("=======");
test(TypeTest.class.getDeclaredField("slist"));
System.out.println("=======");
test(TypeTest.class.getDeclaredField("tlist"));
System.out.println("=======");
test(TypeTest.class.getDeclaredField("t"));
System.out.println("=======");
test(TypeTest.class.getDeclaredField("tarray"));
public static void test(Field field)
if (field.getType().isPrimitive())
System.out.println(field.getName() + "是基本数据类型");
else
System.out.println(field.getName() + "不是基本数据类型");
if (field.getGenericType() instanceof ParameterizedType)
System.out.println(field.getName() + "是泛型类型");
else
System.out.println(field.getName() + "不是泛型类型");
if (field.getType().isArray())
System.out.println(field.getName() + "是普通数组");
else
System.out.println(field.getName() + "不是普通数组");
if (field.getGenericType() instanceof GenericArrayType)
System.out.println(field.getName() + "是泛型数组");
else
System.out.println(field.getName() + "不是泛型数组");
if (field.getGenericType() instanceof TypeVariable)
System.out.println(field.getName() + "是泛型变量");
else
System.out.println(field.getName() + "不是泛型变量");
打印结果:
i是基本数据类型
i不是泛型类型
i不是普通数组
i不是泛型数组
i不是泛型变量
=======
it不是基本数据类型
it不是泛型类型
it不是普通数组
it不是泛型数组
it不是泛型变量
=======
iarray不是基本数据类型
iarray不是泛型类型
iarray是普通数组
iarray不是泛型数组
iarray不是泛型变量
=======
list不是基本数据类型
list不是泛型类型
list不是普通数组
list不是泛型数组
list不是泛型变量
=======
slist不是基本数据类型
slist是泛型类型
slist不是普通数组
slist不是泛型数组
slist不是泛型变量
=======
tlist不是基本数据类型
tlist是泛型类型
tlist不是普通数组
tlist不是泛型数组
tlist不是泛型变量
=======
t不是基本数据类型
t不是泛型类型
t不是普通数组
t不是泛型数组
t不是泛型变量
=======
tarray不是基本数据类型
tarray不是泛型类型
tarray是普通数组
tarray不是泛型数组
tarray不是泛型变量
@Qualifier的使用
定义两个注解
@Target(ElementType.TYPE, ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("random")
public @interface Random
@Target(ElementType.TYPE, ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("roundRobin")
public @interface RoundRobin
定义一个接口和两个实现类,表示负载均衡
public interface LoadBalance
String select();
@Component
@Random
public class RandomStrategy implements LoadBalance
@Override
public String select()
return null;
@Component
@RoundRobin
public class RoundRobinStrategy implements LoadBalance
@Override
public String select()
return null;
使用:
@Component
public class UserService
@Autowired
@RoundRobin
private LoadBalance loadBalance;
public void test()
System.out.println(loadBalance);
@Resource注解底层工作流程
以上是关于Spring源码解读---依赖注入源码解析的主要内容,如果未能解决你的问题,请参考以下文章