共计 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.