关于spring:从源码的角度查找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动态变量及运行后果(动态变量注入失败):

  • 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.

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理