从源码的角度查找Spring @Autowired注解不能依赖注入静态变量的原因

前言

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

一、测试项目

  • AppConfig.java

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

    @Repository
    public class UserDao {
    }
  • UserService.java

    @Service
    public 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静态变量及运行结果(静态变量注入失败):

从源码的角度查找Spring @Autowired注解不能依赖注入静态变量的原因_第1张图片

  • UserService.java中依赖注入UserDao实例实例变量及运行结果(实例变量注入成功):
    从源码的角度查找Spring @Autowired注解不能依赖注入静态变量的原因_第2张图片
  • 添加set方法完成静态变量的依赖注入(静态变量注入成功):

从源码的角度查找Spring @Autowired注解不能依赖注入静态变量的原因_第3张图片
注意: 使用构造方法也可以完成注入,但是使用构造方法来注入并不是@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注解标识的变量进行依赖注入

  • 请先看下图中的注释及源码解释从源码的角度查找Spring @Autowired注解不能依赖注入静态变量的原因_第4张图片
  • org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata源码分析

    private InjectionMetadata buildAutowiringMetadata(final Class clazz) {
        // 存放当前类包括父类中带@Autowired注解的字段和方法
        List elements = new ArrayList<>();
        Class targetClass = clazz;
    
        do {
            // 存放targetClass中所有带了@Autowired注解的字段和方法
            final List 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 @Autowired注解不能依赖注入静态变量的原因_第5张图片从源码的角度查找Spring @Autowired注解不能依赖注入静态变量的原因_第6张图片
至此,spring针对依赖注入的功能的准备工作算是完成了。为什么说是准备工作呢?因为后续还要执行真正的inject注入属性方法,最后会通过Spring的beanFacotry或者直接从cache中拿依赖对象,最后进行属性赋值。至此,Spring @Autowired注解的处理流程就结束了。

四、总结

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

你可能感兴趣的:(javaspring)