关于spring:从源码的角度查找Spring-Autowired注解不能依赖注入静态变量的原因

3次阅读

共计 4456 个字符,预计需要花费 12 分钟才能阅读完成。

前言

  • 本文将总结下 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 动态变量及运行后果 ( 动态变量注入失败):

  • 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.
正文完
 0