前言

  • 本文将总结下Spring依赖注入动态属性失败以及增加set办法就能解决的原理

一、测试项目

  • AppConfig.java

    @Configuration@ComponentScan("com.eugene.sumarry.csdn.autowiredstatic")public class AppConfig {}
  • UserDao.java

    @Repositorypublic class UserDao {}
  • UserService.java

    @Servicepublic class UserService {    @Autowired    private UserDao userDao;    @Override    public String toString() {        return "UserService{" +                "userDao=" + userDao +                '}';    }}
  • Entry.java

    public class Entry {    public static void main(String[] args) {        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);        System.out.println(context.getBean(UserService.class));    }}

二、问题重现及解决方案

  • UserService.java中依赖注入UserDao动态变量及运行后果(动态变量注入失败):

  • UserService.java中依赖注入UserDao实例实例变量及运行后果(实例变量注入胜利):
  • 增加set办法实现动态变量的依赖注入(动态变量注入胜利):


留神: 应用构造方法也能够实现注入,然而应用构造方法来注入并不是@Autowired注解实现的性能,因为你会发现,你加与不加@Autowired注解都会实现注入。此时是通过构造方法进行依赖注入的(如果读者不信,读完上面的 【原理】章节后能够本人debug调试)。本文解说的是@Autowired注解的原理,所以不思考构造方法的依赖注入

三、原理

  • 背景常识: Spring的依赖注入形式有很多种,对Spring而言常见的有如下四种

    常见依赖注入类型备注
    AbstractBeanDefinition.AUTOWIRE_NO不开启主动拆卸性能
    AbstractBeanDefinition.AUTOWIRE_BY_NAME依据变量名来主动拆卸
    AbstractBeanDefinition.AUTOWIRE_BY_TYPE依据类型主动拆卸
    AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR依据构造方法主动拆卸

    而针对于@Autowired注解的依赖注入,最终都会通过AutowiredAnnotationBeanPostProcessor后置处理器来解决,针对于此篇博客而言,是它的MergedBeanDefinitionPostProcessor身份起的作用对于尔后置处理器的作用能够查看我之前公布的博客:spring 5.0.x源码学习系列八: 实例化bean之应用构造方法创立bean、主动拆卸与循环依赖的第三章: spring bean实例化过程中波及到的后置处理器和执行程序。最终,通过AutowiredAnnotationBeanPostProcessor后置处理器的解决,会将以后类的所有反对主动拆卸属性以InjectionMetadata类型的对象保留,那到底是反对哪些属性的主动拆卸的呢?持续往下看。。

3.1 spring如何抉择@Autowired注解标识的变量进行依赖注入

  • 请先看下图中的正文及源码解释
  • org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata源码剖析

    private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {    // 寄存以后类包含父类中带@Autowired注解的字段和办法    List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();    Class<?> targetClass = clazz;    do {        // 寄存targetClass中所有带了@Autowired注解的字段和办法        final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();        ReflectionUtils.doWithLocalFields(targetClass, field -> {            // JDK1.8 新个性,传入一个办法进去,在办法外部就是获取以后类的所有字段(不包含父类,            // 包含本人定义的公有变量),并循环调用传入的办法,            // 即以后办法            // 判断以后字段是否有@Autowired注解            AnnotationAttributes ann = findAutowiredAnnotation(field);            if (ann != null) {                // 判断以后字段是否为static润饰  ===>  动态变量, 如果是,则只是返回了                if (Modifier.isStatic(field.getModifiers())) {                    if (logger.isWarnEnabled()) {                        logger.warn("Autowired annotation is not supported on static fields: " + field);                    }                    return;                }                boolean required = determineRequiredStatus(ann);                // 将字段包装成AutowiredFieldElement对象,并存入一开始创立的list中                currElements.add(new AutowiredFieldElement(field, required));            }        });        ReflectionUtils.doWithLocalMethods(targetClass, method -> {            // 同上,此时获取的是以后class外部的method(不包含父类, 包含本人定义的公有办法            // ),并挨个遍历执行以后传入的办法            // 判断遍历的办法是否为桥接办法            Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);            if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {                return;            }            // 拿到以后办法的@Autowired注解,并进行校验拿到实在办法(因为有可能以后要解决的类是一个代理对象,或者接口等等)            AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);            if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {                // 判断以后办法是否为静态方法                if (Modifier.isStatic(method.getModifiers())) {                    if (logger.isWarnEnabled()) {                        logger.warn("Autowired annotation is not supported on static methods: " + method);                    }                    return;                }                if (method.getParameterCount() == 0) {                    if (logger.isWarnEnabled()) {                        logger.warn("Autowired annotation should only be used on methods with parameters: " +                                method);                    }                }                boolean required = determineRequiredStatus(ann);                // 找到以后办法中的参数形容器                PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);                // 将set办法和要传入的属性的形容器包装成AutowiredMethodElement类并增加至一开始创立的list汇合中                currElements.add(new AutowiredMethodElement(method, required, pd));            }        });        elements.addAll(0, currElements);        // 获取父类的class,针对父类的属性和办法在做筛选        targetClass = targetClass.getSuperclass();    }    while (targetClass != null && targetClass != Object.class);    // 最终返回一个InjectionMetadata对象,其中蕴含以后类及其父类(不蕴含Object类)的所有带    // @Autowired注解的办法和子弹    return new InjectionMetadata(clazz, elements);}    
  • 综上,AutowiredAnnotationBeanPostProcessor后置处理器的MergedBeanDefinitionPostProcessors后置处理器的作用就是将以后类及其父类(不蕴含Object类)的所有蕴含@Autowired注解的非动态字段和非动态带参办法以InjectionMetadata对象的形式保留在injectionMetadataCache属性中

3.2 spring在进行依赖注入时的逻辑

  • 在进行依赖注入时,必定是执行了populateBean办法,具体后果如下图所示:


至此,spring针对依赖注入的性能的筹备工作算是实现了。为什么说是筹备工作呢?因为后续还要执行真正的inject注入属性办法,最初会通过Spring的beanFacotry或者间接从cache中拿依赖对象,最初进行属性赋值。至此,Spring @Autowired注解的解决流程就完结了。

四、总结

  • 综上所述,针对@Autowired注解的解决流程次要外围为AutowiredAnnotationBeanPostProcessor后置处理器的MergedBeanDefinitionPostProcessor身份,它会去筛选出所有带@Autowired注解的非动态字段和非静态方法作为候选者,最终再通过spring的bean工厂去获取依赖的对象,应用反射的技术实现注入
  • I am a slow walker, but I never walk backwards.