【Spring源码核心篇-02】深入理解spring的依赖注入和属性填充
Spring源码核心篇整体栏目
内容 | 链接地址 |
---|---|
【一】Spring的bean的生命周期 | https://zhenghuisheng.blog.csdn.net/article/details/143441012 |
【二】深入理解spring的依赖注入和属性填充 | https://zhenghuisheng.blog.csdn.net/article/details/143854482 |
深入理解spring的依赖注入和属性填充
如需转载,请附上链接:https://blog.csdn.net/zhenghuishengq/article/details/143854482
一,深入理解spring的依赖注入
在前面这篇文章中,讲解了spring的bean生命周期,在bean的创建过程中,有一部属性填充的操作,其实就是内部的实现原理就是spring的依赖注入,比如在一个类中加了一个 @Autowired、@Resource 等注解等,spring容器就会通过这种依赖注入的方式,将对应的属性进行填充。本文的主题就是spring的依赖注入的底层源码是如何实现的,因此在熟悉本文之前,最好先了解本人写的第一篇文章
1,寻找注入点
在bean的生命周期中,在实例化之后和属性填充之前,会有一个bean的后置处理器的操作,会对@Autowire,@Value,@Resource等注解进行一个预解析,就是提前的将带有@Autowired,@Value,@Resource的属性或者方法找到,然后进行预解析
依旧是得进入这个 doCreateBean 方法,进入到这个 applyMergedBeanDefinitionPostProcessors 的bean的后置处理器里面,用于对 @Autowired和@Value等进行预解析
将autowired注入点找到的方法是 postProcessMergedBeanDefinition ,最终将属性封装成InjectedElement类型
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
// 寻找bean中所有被@Autowired注释的属性,并将属性封装成InjectedElement类型
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
从元数据信息中去寻找有关@Autowired,@Value和@Inject等相关注解,具体如何去封装成这个 InjectionMetadata 对象的,详情还需要看这个 buildAutowiringMetadata 方法内部的具体实现,在方法上的注入点以及在字段上面的注入点的实现也有所差别
最终进入这个 buildAutowiringMetadata 方法内部,可以发现要找某个类的属性,在spring底层有两种方式实现,一种是通过反射的方式找每个属性、另一种是通过本地方法的布局变量找属性 ,最终将所有带有 @Autowired的注解的属性或者方法全部找出,至此注入点全部被找到
2,populateBean依赖注入
在上面的这个bean的后置处理器找到全部的注入点之后,依旧得进入到这个 doCreateBean 方法内部,进入到这个属性填充的方法,可以参考前一篇
populateBean(beanName, mbd, instanceWrapper);
进入这个populateBean方法之后,在属性填充之前会有一个后置处理,用于给bean提前赋值的机会,如果某个类已经实现了 postProcessAfterInstantiation 接口,并且提前给某些要注入的@Autowire等对象就进行了提前赋值,那么这段属性注入的代码就会提前结束
继续往下看这个方法,最终会有一个重要的方法 postProcessPropertyValues ,对依赖的对象进行后置处理
然后进入Autowired的实现类中,里面会去解析所有上面拿到的与解析的value属性
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
// 从缓存中拿到注解元数据, 缓存没有载解析一遍
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 注入
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
最后进入这个 inject 方法,就是一个真正的就行依赖注入的方法
在这个inject方法中,由于@Autowired属性可以加在字段上面或者加在方法上面,因此在解析这个注解时会有两个具体的实现类,一个是用于解析字段的实现类,一个是用于解析方法的实现类
2.1,字段的属性注入
在使用的字段属性的注入方式也是实际开发中用的最多的一种方式,如在OrderService中注入一个UserService
@Service
public class OrderService{
@Autowired
private UserService userService;
}
接下来进入字段的inject的方法中,第一次进入肯定是不在缓存中,所以看下面的else分支,首先会构建一个描述对象 DependencyDescriptor ,是专门用于注入的包装类,然后进入下面最重要的方法 resolveDependency ,通过这个beanFactory去bean工厂中找bean。这要核心就是根据对应的字段,去工厂中找对应的bean
接下来进入这个核心参数的方法resolveDependency 中,起流程如下
- 首先第一步会初始化参数,这个参数可能是方法中参数名称,也可能是字段名称,因为这个方法是公共方法
- 第二步就是对一些属性进行判断,如判断是不是optional类等,都是一些分支逻辑,先不看
- 进入最后一个else,首先会判断属性上面或者方法前面是否有Lazy注解,有的话那就需要创建一个代理对象,这样就解决了延时的问题,通过创建的代理对象,在使用的时候在去bean工厂中获取
- 最终进入这个 doResolveDependency 方法,真正的去解析这个属性依赖
接下来就是进入这个重点接下依赖的方法 doResolveDependency,拿到对应的注入点
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// 注入点
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
...
}
2.1.1,@Value的处理
接下来依旧是这个 doResolveDependency 方法里面的逻辑,首先第一步是解析关于@Value的逻辑
- 首先第一步通过调用 getSuggestedValue 方法获取@Value是在属性上还是在方法的参数上面
- 第二步就是解析占位符和一些el表达式,如有#(“xxx”)占位符,$(“xxx”)占位符等
- 最后一步就是,进行一个类型转换,默认是String类型,但是具体的也可能会转成对应类等
//解析@Value注解的额
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
if (value instanceof String) {
// 2.1 占位符解析
String strVal = resolveEmbeddedValue((String) value);
BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
// 2.2 Spring EL 表达式
value = evaluateBeanDefinitionString(strVal, bd);
}
// 2.3 类型转换
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
return (descriptor.getField() != null ?
converter.convertIfNecessary(value, type, descriptor.getField()) :
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
}
再来看一下上面的第一步的getSuggestedValue 方法,通过这个 DependencyDescriptor 依赖的描述器,先去属性中获取这个value,如果没找到,那么在去方法的参数去获取value
@Override
@Nullable
public Object getSuggestedValue(DependencyDescriptor descriptor) {
Object value = findValue(descriptor.getAnnotations());
if (value == null) {
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
value = findValue(methodParam.getMethodAnnotations());
}
}
return value;
}
其基本使用如下
@Value("$xxx")
@Value("#xxx")
test(@Value("xxx") String aaa)
2.1.2,@Autowired处理
接下来依旧是这个 doResolveDependency 方法里面,先通过beanName的方式去工厂中获取对象,先通过类型去找bean
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
// 4.1 若在容器中没有查找到依赖,判断descriptor.require
if (matchingBeans.isEmpty()) {
// 如果 @autowire 的 require 属性为 true ,但是没有找到相应的匹配项,则抛出异常
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
然后接着会做一个判断,如果先根据type类型找到了多个bean,那么就会进行一个过滤,将最终的那个属性找出来
对应的 determineAutowireCandidate 方法如下:@Primary -> @Priority -> 方法名称或字段名称匹配
- 首先会判断是否有这个 @Primary 优先级的注解,有的话则直接返回
- 在判断是否会有这个 @Priority 注解,有的话也直接返回
- 最后根据属性名称或者方法中的参数名字进行相应的匹配,最后将结果返回
- 如果都没有的话,那么则直接返回一个null
最后拿到这个 instanceCandidate 实例,通过调用这个 resolveCandidate 方法,内部通过调用这个 getBean 方法进行实例化
2.2,方法的属性注入
通过方法的属性注入时,一般就是注入方法中参数的属性
@Service
public class OrderService{
private UserService userService;
private void setOrderService(UserService userService){
this.userService = userSercvice;
}
}
接下来就是进入方法的inject的实现类中
public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
...
}
直接看这个inject的实现方法,首先是判断@Autowired标记是否在方法上面,和字段中的核心属性一样,都是调用这个 resolveDependency 方法,所以后序的只需要看懂这个字段注入的即可
2.3,@Resource处理
依旧是得回到 populateBean 的方法中,然后看到内部的这个postProcessPropertyValues ,对依赖的对象进行后置处理
在上面分析的@Autowired是在 AutowiredAnnotationBeanPostProcessor 的bean的后置处理器中,而在现在要分析的@Resource注解的后置处理器是在 CommonAnnotationBeanPostProcessor 类中
那看这个@Resouce的属性注入的流程和上面的就差不多了,首先也是调用这个findResourceMetadata的方法去寻找注入点,然后将全部找到的属性封装成一个元数据,最后调用inject方法进行属性赋值
找这个元数据的方式和@Autowired方式一样,都是通过反射的方式和本地方法中的局部变量去拿数据,不同的是在一些分支中,如属性中加static在@Autowired是不做任何处理,但是在@Resource中是会抛出异常的
然后除了找这个Resouce注解的属性方式不同之外,后序调用inject方法,以及判断是在属性上还是方法上,其内部的流程都是一样的
最后看到这个 autowireResource 方法,可以发现使用这个使用resource是先通过byName的方式找bean,最后再通过byType的方式找bean
@Resource是jdk层面的注解,@Autowire注解时spring层面的注解,如果后期框架有变动的话,优先可以考虑使用@Resource注解,二者在性能方面是没有区别的
原文地址:https://blog.csdn.net/zhenghuishengq/article/details/143854482
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!