Spring源码讲解(四)Spring中bean的属性注入

迷南。 2022-11-24 12:58 295阅读 0赞

由于Spring源码过于庞大,文章中不会列出细节,须要大家花时间下去研究哦

获取要注入的属性的源信息

当BeanFactory(AbstractAutowireCapableBeanFactory)调用doCreateBean方法实例化完一个对象之后,会调用第三次后置处理器:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 1

可以看到第三次后置处理器是找寻实现了MergedBeanDefinitionPostProcessor接口的后置处理器执行其postProcessMergedBeanDefinition方法,Spring中关键的两个实现了这个接口的后置处理器是以下两个:

AutowiredAnnotationBeanPostProcessor(处理@AutoWired/@Valued注解的属性注入)

CommonAnnotationBeanPostProcessor(处理@Resource注解的属性注入)

首先来看下这两个后置处理器的源码:

AutowiredAnnotationBeanPostProcessor

AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition ->

AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata方法

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 2

这里通过buildAutowiringMetadata方法获取到了一个InjectionMetadata对象,最后放到了injectionMetadataCache这个Map中,后面这个map,顾名思义就是当作缓存来用的,我们主要看下这个buildAutowiringMetadata方法:

最重要的一段是调用了ReflectionUtils#doWithLocalFields方法:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 3

ReflectionUtils#doWithLocalFields方法主要是通过反射获取到了所有类成员属性Fileds,然后依次调用FieldCallback#doWith方法,我们可以看到实际上这个FieldCallback其实就是外面传进来的一段lambda表达式。通过findAutowiredAnnotation方法获取到AnnotationAttributes对象,放到InjectedElement的list当中(最终这个list会被包装为一个一个InjectionMetadata对象被缓存的上面的那个map中去)。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 4

findAutowiredAnnotation方法就是判断当前Field上面有没有加了@Autowired或者@Value注解,如果加了就获取到注解的源信息,就是上面的AnnotationAttributes对象。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 5

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 6

除了查询加了注解的属性,还会查询加了注解的方法,在执行完ReflectionUtils#doWithLocalFields方法之后,还会执行ReflectionUtils#doWithLocalMethods方法,不同的是解析完之后是AutowiredMethodElement对象存起来的。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 7

所以AutowiredAnnotationBeanPostProcessor这个后置处理器就是用来缓存加上了@Autowired或者@Value注解的属性或者方法的源信息的,方便后面注入属性时知道须要注入哪些属性。

CommonAnnotationBeanPostProcessor

流程同AutowiredAnnotationBeanPostProcessor,唯一的不同就是扫描的是@Resource注解,最终存放的InjectedElement是ResourceElement类型的

CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition ->

CommonAnnotationBeanPostProcessor#findResourceMetadata方法 ->

CommonAnnotationBeanPostProcessor#buildResourceMetadata方法

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 8

走完第三次的后置处理器之后,那些须要注入的属性或者方法信息就被作为InjectionMetadata对象放到injectionMetadataCache这个map中,后面的属性注入就会用到这个map了。

属性注入

spring的属性注入是在执行完第三此后置处理器之后,就立即执行的,也是在doCreateBean方法中。会调用populateBean方法来执行属性的注入。

populateBean方法的第一段逻辑是执行spring的第五次后置处理器,这个后置处理器主要是确定是否须要注入属性,false表示忽略属性的注入,spring不建议对外使用这个接口,因为代表着这个bean将不再维护内部的属性。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 9

之后判断当前bd如果装配模型是AUTOWIRE_BY_NAME或者AUTOWIRE_BY_TYPE时须要做的一些事情。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 10

处理完AUTOWIRE_BY_NAME或者AUTOWIRE_BY_TYPE的两种情况之后,就是要注入@Autowired注解和@Resource注解标识的bean了,这里就要执行spring的第六次后置处理器InstantiationAwareBeanPostProcessor:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 11

这里关键的两个后置处理器又是之前生成InjectionMetadata的两个后置处理器AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor(他们既实现了MergedBeanDefinitionPostProcessor接口又实现了InstantiationAwareBeanPostProcessor接口)。

看下这两个后置处理器的源码:

AutowiredAnnotationBeanPostProcessor

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 12

通过findAutowiringMetadata方法获取须要注入的属性,这里面就是从之前的injectionMetadataCache这个map中直接拿这个InjectionMetadata对象,这个对象中包含了当前bean所有须要注入的属性(通过@Autowired注解标识的)。然后调用InjectionMetadata#inject方法将当前的bean对象(前面通过反射创建的原生对象);bean的名称;以及pvs传进去,这里的pvs是通过AbstractBeanDefinition#setPropertyValues方法设置的额外属性:

2020080611510155.png

InjectionMetadata#inject方法中遍历了所有的InjectedElement元素,执行其inject方法:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 13

从前面的第三次后置处理器中可以知道,这里的InjectedElement对象可能是AutowiredFieldElement或者是AutowiredMethodElement,我们这里以AutowiredFieldElement为例吧。

AutowiredFieldElement#inject方法:

20200806115208983.png

通过DefaultListableBeanFactory#resolveDependency方法获取到真正的对象,然后利用反射赋到当前的bean中去:

20200806115241962.png

所以我们看下这个resolveDependency做了什么:

DefaultListableBeanFactory#resolveDependency方法

resolveDependency方法顾名思义就是解决依赖注入:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 14

point1:

这里会判断当前的这个属性有没有加@Lazy注解,加了的话先返回一个代理的对象出来,后面用时再实例化真正的对象,这里不会再去注入真正的属性。

point2:

真正的去注入属性。

看下这个doResolveDependency方法:

DefaultListableBeanFactory#doResolveDependency方法 ->

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 15

这里是解析属性类型是List等其他特殊类型的时候的处理方式,暂时过滤掉,下面再讲。

20200806115554374.png

后面就是执行DfaultListableBeanFactory#findAutowireCandidates方法:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 16

看看里面是如何实现的:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 17

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 18

所以上面获取到的matchingBeans就是根据bean的类型从DefaultListableBeanFactory中获取到的所有的以bean名称为key,bean的类型为value的一个map!

当然matchingBeans可能会出现多个的情况,再回到doResolveDependency方法中,这里处理了有多个匹配的bean的情况:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 19

可以看到最后还是通过名称从容器中取出来这个bean来完成属性注入的:

DependencyDescriptor#resolveCandidate方法

20200806120030276.png

上面的doResolveDependency方法中还有一个resolveMultipleBeans方法没有讲,这里是处理属性类型是List等其他特殊类型的情况的:

数组和集合:

@AutoWired

private Object[] objArray;

@AutoWired

private List objList;

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 20

map的情况:

@AutoWired

private Map objList;

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 21

从上面可以看到内部也是调用findAutowireCandidates方法来找到bean的,不同的是这种情况下,当走到addCandidateEntry方法是会走第一个逻辑快,直接返回bean对象出来:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 22

再看下这个DependencyDescriptor#resolveCandidate方法:

20200806140132633.png

这里也可以看出来如果是Map的话,他的key是bean的名称,value是bean对象!

这里通过阅读源码可以知道Spring处理@Autowired注解的属性注入时还是和平常我们理解的不太一样的:

1.先通过类型获取bean,如果获取到多个的时候,不是通过名称从容器中去获取,而是从之前通过类型获取的多个bean(这里的bean其实只是bean的Class对象)中再去通过名称匹配

2.不管@Autowired是通过类型还是通过名称获取,其最终都是通过名称从容器中取出来这个bean来完成属性注入的

CommonAnnotationBeanPostProcessor

开头同AutowiredAnnotationBeanPostProcessor,当走到InjectedElement#inject方法时,因为InjectedElement对象类型是ResourceElement类型(前面有分析到),他的inject方法其实就是父类InjectedElement的inject方法:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 23

我们还是以注入对象是属性的情况为例,看下getResourceToInject方法,这个抽象方法,是ResourceElement实现的:

20200806140439624.png

可以看到这里和AutowiredAnnotationBeanPostProcessor一样,首先还是判断了是不是加了@Lazy注解,下面的getResource方法调用链:

CommonAnnotationBeanPostProcessor#getResource方法 ->

CommonAnnotationBeanPostProcessor#autowireResource方法

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE1MzU1NDE_size_16_color_FFFFFF_t_70 24

所以分析源码可以看到当属性的名称在spring容器中存在时,直接从容器中取出这个bean,不存在则调用DefaultListableBeanFactory#resolveDependency方法,这个方法已经很熟悉了,就是之前的AutowiredAnnotationBeanPostProcessor那一套(就是先通过类型,不能唯一匹配的话,再通过名称)。所以@Resource注解确实是先按照名称取匹配,匹配不到再按照类型去匹配。

发表评论

表情:
评论列表 (有 0 条评论,295人围观)

还没有评论,来说两句吧...

相关阅读