自学内容网 自学内容网

【Spring源码核心篇-02】深入理解spring的依赖注入和属性填充

Spring源码核心篇整体栏目


内容链接地址
【一】Spring的bean的生命周期https://zhenghuisheng.blog.csdn.net/article/details/143441012
【二】深入理解spring的依赖注入和属性填充https://zhenghuisheng.blog.csdn.net/article/details/143854482

如需转载,请附上链接: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)!