Spring注解@Value解读

本文将主要通过源码解读来分析@Value实现属性注入Spring Bean的过程,并对static类型字段无法通过@Value注入为Spring Bean依赖的原因做一个探究。

依赖注入概述

基于Spring MVC或者Spring Boot开发后端项目的时候总是绕不开Spring IOC容器,Spring IOC容器管理Spring Bean,我们可以通过XML或者注解的方式来定义一个Bean,如通过注解@Service,@Controller,@Component,@Repository或者@Bean加Java配置方式。实际应用中我们定义的一个Bean很多时候存在互相之间的依赖,比如Service层通过@Service定义的Bean往往要依赖数据库DAO层通过@Repository定义的Bean,这时候我们往往通过@Autowired或@Resource来自动装配,另外还有一些Bean内部的属性(Field)需要通过配置文件中定义的值来设置,而后创建的Bean才是符合我们预期的,我们一般通过@Value和@ConfigurationProperties来实现属性的注入。

实际应用案例

以文档管理服务为例,我们有个资源上传的接口,其依赖一个FeignClient客户端Bean,一个业务逻辑处理的Service Bean,还依赖一个资源上传的OSS的桶bucket成员变量,bucket和OSS我们通常配置在配置文件中以区分不同环境。具体的依赖关系如下图:
Spring注解@Value解读_第1张图片

源码解读分析

首先看一下Spring Bean的的整个生命周期:
Spring注解@Value解读_第2张图片
在实例化对象完成后,设置属性值(polulateBean)之前,会搜集类上的注解元数据信息,然后在polulalteBean中拦截,执行BeanPostProcessor中的方法,反射注入依赖的值。@Resource是jdk提供的注解,其使用的后置处理器是CommonAnnotationBeanPostProcessor,而@Autowired和@Value注解使用的后置处理器是AutowiredAnnotationBeanPostProcessor,具体的我们下面会通过代码来解读。
先来看一下BeanFactory和ApplicationContext在装载bean时候的区别:

  • BeanFactory在启动时不会去实例化Bean,当从容器中拿Bean的时候才会去实例化;
  • ApplicationContext在启动时就把所有的Bean全部实例化了。可以为Bean配置lazy-init=true来让Bean延迟实例化。

我们下面以Spring Boot和ApplicationContext来走读一下源码:
1、首先进入SpringApplication.run(XXXApplication.class, args),然后一直进入到SpringApplication的refreshContext()方法,refreshContext()是IOC容器初始化的核心方法,完成容器上下文的刷新,主要包括各种处理器、监听器以及Bean的初始化等工作。
Spring注解@Value解读_第3张图片
2、然后进入到AbstractApplicationContext的refresh()方法中的finishBeanFactoryInitialization(beanFactory)方法,这个方法就是ApplicationContext完成Spring Bean实例化和初始化的关键方法。
Spring注解@Value解读_第4张图片
3、而后进入到DefaultListableBeanFactory的preInstantiateSingletons()方法,此方法就是获取到所有的beanName,然后循环的去实例化+初始化bean,而实例化+初始化的方式就是调用其getBean(String name)方法,因此我们直接调到getBean(name) ,此方法的最终实现是在AbstractBeanFactory中,我们进入AbstractBeanFactory的getBean(beanName)查看,然后根据调用栈直接进入到AbstractBeanFactory的doGetBean(final String name, @Nullable final Class requiredType, @Nullable final Object[] args, boolean typeCheckOnly)方法中。这个方法代码较多,我们直接查看我们要关注的核心代码段如下:
Spring注解@Value解读_第5张图片
4、然后我们进入到AbstractAutowireCapableBeanFactory的createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法中,其中AbstractAutowireCapableBeanFactory是AbstractBeanFactory的一个子类,AbstractBeanFactory的createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)由AbstractAutowireCapableBeanFactory实现。createBean方法的核心是调用了AbstractAutowireCapableBeanFactory的doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args),我们直接查看doCreateBean方法。
Spring注解@Value解读_第6张图片
5、我们略过创建实例的过程,直接进入applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class beanType, String beanName)方法。
在这里插入图片描述
5.1、applyMergedBeanDefinitionPostProcessors方法主要是循环所有后置处理器对beanDefinition做处理,我们之前说过@Value注解是通过AutowiredAnnotationBeanPostProcessor来处理的,我们直接进入AutowiredAnnotationBeanPostProcessor的postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName)方法如下:
在这里插入图片描述
其中的InjectionMetadata如下,它主要封装了bean的全限定类名和这个类中所有带有注解的成员属性(字段或者其他bean)描述(PropertyDescriptor,可以简单理解为带注解的成员属性和注解本身两者的组合),当然也就包括由@Value注解的字段。
Spring注解@Value解读_第7张图片
5.2、我们继续进入到findAutowiringMetadata(String beanName, Class clazz, @Nullable PropertyValues pvs)方法,通过buildAutowiringMetadata(final Class clazz)首先获取了当前bean所属类的属性注解元数据,然后存储到了injectionMetadataCache缓存中,完成了applyMergedBeanDefinitionPostProcessors方法。
Spring注解@Value解读_第8张图片
5.3、我们进入到buildAutowiringMetadata(final Class clazz)查看构建bean注入注解元数据的过程
Spring注解@Value解读_第9张图片
其中用到了获取当前成员属性AnnotationAttributes的方法findAutowiredAnnotation(AccessibleObject ao)如下:
Spring注解@Value解读_第10张图片
其中autowiredAnnotationTypes表示后置处理器AutowiredAnnotationBeanPostProcessor处理的注解列表,初始化如下:
Spring注解@Value解读_第11张图片
6、第5步完成了AbstractAutowireCapableBeanFactory的doCreateBean方法中的applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class beanType, String beanName),即完成了bean的成员属性注解元数据的本地缓存。可以理解applyMergedBeanDefinitionPostProcessors为一个拦截器,即在对bean进行成员属性和依赖注入前先预处理bean的依赖和成员属性数据的解析和缓存,而后再进行populateBean。
6.1、进入到AbstractAutowireCapableBeanFactory的populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)方法,我们直接看核心代码如下,其中postProcessProperties方法就是完成了bean的成员属性的初始化和依赖的注入过程。
Spring注解@Value解读_第12张图片
6.2、因为我们要处理的是@Value和@Autowired注解的bean依赖,AutowiredAnnotationBeanPostProcessor是InstantiationAwareBeanPostProcessor接口的实现类,所以上面代码端循环过程中会进入到之前的AutowiredAnnotationBeanPostProcessor的postProcessProperties(PropertyValues pvs, Object bean, String beanName)。
Spring注解@Value解读_第13张图片
6.3、上面findAutowiringMetadata方法就是第5.2步的方法,也就是从缓存Map中取出bean对应的注解元数据,而后调用InjectionMetadata的inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs)方法执行依赖注入过程。
Spring注解@Value解读_第14张图片
6.4、在上面对注解元数据列表循环处理(bean依赖了多个其他的bean或者多个成员属性通过配置注入)进行依赖注入,调用element.inject(target, beanName, pvs)方法。而AutowiredAnnotationBeanPostProcessor类的内部类AutowiredFieldElement继承了InjectionMetadata.InjectedElement类,并重写了其inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)方法,AutowiredAnnotationBeanPostProcessor类的内部类AutowiredMethodElement也继承了InjectionMetadata.InjectedElement类,并重写了其inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)方法,因此6.3中的inject调用将会在此子类中执行,AutowiredFieldElement处理成员属性上注解,AutowiredMethodElement处理方法上注解。因此@Value注解的处理将会进入到AutowiredFieldElement的inject方法。
Spring注解@Value解读_第15张图片
6.5、首先通过beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter)方法解析依赖,然后注册当前bean依赖的其他bean,最后通过反射将依赖注入到当前bean中完成bean的依赖注入过程。DefaultListableBeanFactory继承了AbstractAutowireCapableBeanFactory类,我们先查看DefaultListableBeanFactory的resolveDependency方法,其中主要是调用了DefaultListableBeanFactory的doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter)方法,我们直接看doResolveDependency方法。
Spring注解@Value解读_第16张图片
6.6、以上解析@Value注解,getSuggestedValue返回的值就是${xxx.documentcenter.bucketName:ssdocumentcenterstatic},因为是字符串,所以进入到下面的逻辑AbstractBeanFactory的resolveEmbeddedValue(@Nullable String value)方法中。resolveEmbeddedValue将完成@Value注解值的解析过程,最终得到配置文件中配置的值ssdocumentcenterstatic。下面的TypeConverter的convertIfNecessary主要完成数据类型的转换,比如配置文件中配置的Boolean类型,将String转为Boolean等。最后返回value值并注入到bean中,此成员属性注入到bean的过程就完成了。我们简单看一下resolveEmbeddedValue解析过程,主要通过AbstractPropertyResolver解析器完成,其内部使用了默认的占位符如下:
Spring注解@Value解读_第17张图片
通过其doResolvePlaceholders(String text, PropertyPlaceholderHelper helper)方法完成解析,内部调用了PropertyPlaceholderHelper的parseStringValue(String value, PlaceholderResolver placeholderResolver, Set visitedPlaceholders)方法来具体实现注解的值的解析和占位替换。解析首先是从配置文件对应的PropertySource中完成配置和值读取,最终通过MapPropertySource实现。

你可能感兴趣的:(Java微服务,java,spring,后端,spring,boot)