前言
- 本文将总结下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 根据构造方法自动装配 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
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针对依赖注入的功能的准备工作
算是完成了。为什么说是准备工作呢?因为后续还要执行真正的inject
注入属性方法,最后会通过Spring的beanFacotry或者直接从cache中拿依赖对象,最后进行属性赋值。至此,Spring @Autowired注解的处理流程就结束了。
四、总结
- 综上所述,针对@Autowired注解的处理流程主要核心为AutowiredAnnotationBeanPostProcessor后置处理器的MergedBeanDefinitionPostProcessor身份,它会去筛选出所有带@Autowired注解的非静态字段和非静态方法作为候选者,最终再通过spring的bean工厂去获取依赖的对象,使用反射的技术完成注入
- I am a slow walker, but I never walk backwards.