前言
- 基于上篇文章从源码的角度查找Spring @Autowired注解不能依赖注入静态变量的原因的启发,我觉得还有必要再总结下spring中
@Autowired
注解的原理。本人在未读spring源码时一直认为@Autowired
的依赖注入模式是byType,直到我看了源码我才打破了这个误区!
一、Spring依赖注入类型的基础知识
-
在上篇博客中也有提到,spring的依赖注入主要包含如下几个方面:
常见依赖注入类型 对应的值 备注 AbstractBeanDefinition.AUTOWIRE_NO 0 不开启自动装配功能 AbstractBeanDefinition.AUTOWIRE_BY_NAME 1 根据变量名来自动装配 AbstractBeanDefinition.AUTOWIRE_BY_TYPE 2 根据类型自动装配 AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR 3 根据构造方法自动装配 AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT 直接忽略。。。 源码中提示在spring 3.0 之后就弃用了(具体看下面的源码),所以这里就不列出它具体的原理了(其实是自己不知道。。。。) -
spring 3.0弃用AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT源码
/** * Constant that indicates determining an appropriate autowire strategy * through introspection of the bean class. * @see #setAutowireMode * @deprecated as of Spring 3.0: If you are using mixed autowiring strategies, * use annotation-based autowiring for clearer demarcation of autowiring needs. */ @Deprecated public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
二、如何判断一个bean的依赖注入类型
- 步骤如下:
- 第一:获取到bean对应的
BeanDefinition
对象(可以通过后置处理器(eg: BeanFactoryPostProcessor)获取bean工厂,进而获取到beanDefinition) - 第二:强转成GenericBeanDefinition
- 第三:调用
getResolvedAutowireMode()
方法
- 第一:获取到bean对应的
- 或者直接在populateBean()方法中进行断点调试,这里我选择断点调试,废话不多说,上项目证明!
三、项目测试
3.1 项目预览
- AppConfig.java
@Configuration @ComponentScan("com.eugene.sumarry.csdn.autowired") public class AppConfig { }
- 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
@Service public class UserService { @Autowired private UserDao userDao; @Override public String toString() { return "UserService{" + "userDao=" + userDao + '}'; } }
- UserDao.java
@Repository public class UserDao { }
3.2 运行测试
spring会有两个地方完成自动装配,:
第一:构造方法自动装配
第二:populateBean方法中进行自动装配
咱们本次总结的是@Autowired注解,忽略构造方法部分。接下来,我们直接运行main方法,并定位到populateBean方法的关键部分
- 条件断点调试:
在这里插入图片描述
从上述图中可以看到,从beanDefinition中获取的依赖注入的属性为0
, 由上述表格中可知它的值为:AbstractBeanDefinition.AUTOWIRE_NO,其实我们可以在AbstractBeanDefinition类中查看autowireMode
属性,它的默认值就是:AUTOWIRE_NO,如图所示:
在这里插入图片描述
,可能有人觉得我在忽悠,之前不是说要通过getResolvedAutowireMode()
方法来获取么,那咱们再列出下getResolvedAutowireMode()
的源码
那/** * Return the resolved autowire code, * (resolving AUTOWIRE_AUTODETECT to AUTOWIRE_CONSTRUCTOR or AUTOWIRE_BY_TYPE). * @see #AUTOWIRE_AUTODETECT * @see #AUTOWIRE_CONSTRUCTOR * @see #AUTOWIRE_BY_TYPE */ public int getResolvedAutowireMode() { // *************看这里************** // 用到了上述所说的autowireMode属性, 默认值为 // AUTOWIRE_NO, 所以直接走else return了 if (this.autowireMode == AUTOWIRE_AUTODETECT) { Constructor<?>[] constructors = getBeanClass().getConstructors(); for (Constructor<?> constructor : constructors) { if (constructor.getParameterCount() == 0) { return AUTOWIRE_BY_TYPE; } } return AUTOWIRE_CONSTRUCTOR; } else { return this.autowireMode; } }
@Autowired
注解不是byType
来依赖注入的,那它的原理是什么呢?这里还要结合上一篇文章: 从源码的角度查找Spring @Autowired注解不能依赖注入静态变量的原因的结论。在spring内部,@Autowired
注解的功能是通过一个叫AutowiredAnnotationBeanPostProcessor的后置处理器来处理的。上篇文章介绍的是它MergedBeanDefinitionPostProcessors的身份(就是将当前类及其父类(不包含Object类)的所有包含@Autowired注解的字段和方法以InjectionMetadata对象的方式保存在injectionMetadataCache
属性中)。在上篇文章中,我们没有总结@Autowired注解的依赖注入逻辑,现在咱们来补上。
四、AutowiredAnnotationBeanPostProcessor的InstantiationAwareBeanPostProcessor身份
- 是的,你没看错,现在要看AutowiredAnnotationBeanPostProcessor的另外一个身份了。我们回到
populateBean
方法,继续调试代码,如下图所示:
在这里插入图片描述
此时我们跟着断点走,进入到AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues方法,如下图所示:
在这里插入图片描述
我们继续执行,将断点打到方法的出口处,如下图所示:
在这里插入图片描述
综上所述,@Autowired
注解的依赖注入就这样完成了(AutowiredAnnotationBeanPostProcessor在处理依赖注入时,从bean工厂中去获取,首先是根据字段的类型去找符合条件的bean,若得到的bean有多个,则找出有@Primary注解修饰的bean,若都没有,则退化成@Resource注解的功能,即根据字段名去寻找bean,若都没有,则会抛出找到多个bean的异常。可以定位到org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject方法细看注入逻辑
)。
这里顺便提一句:不知道大家看到本章(第四章)
第一张图的如下代码没:
有一个for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null) { return; } } }
return
关键字,假设我们写一个实现了InstantiationAwareBeanPostProcessor
接口的bean,那么是不是可以破坏掉@Autowired
注解的功能?当然,这里有个注意事项:就是自己写的这个bean的处理顺序要在AutowiredAnnotationBeanPostProcessor前面。
,我自己做了这么个实验,在项目中添加了如下代码后,@Autowired
注解的功能就失效了(我测试的时候此后置处理器比AutowiredAnnotationBeanPostProcessor先执行。spring版本为:5.0.x )
当然,除了这个方式后,添加如下代码后也能使@Component public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { if (beanName.equals("userService")) { return null; } return pvs; } }
@Autowired
注解的功能失效,
上述的方式主要是利用了@Component public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { return false; } }
populateBean
方法的下面一段代码:if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } } }
细心的小伙伴应该能发现,这两种方式的区别就在于实现的是不同的方法,但是实现的是同一个接口**InstantiationAwareBeanPostProcessor**,所以,我们这里也能得出一个结论:`InstantiationAwareBeanPostProcessor`类型的后置处理器可以让spring`@Autowired`注解功能失效, 具体体现在`postProcessPropertyValues`和`postProcessAfterInstantiation`方法。
五、总结
-
看到这里,咱们以后可别再说
@Autowired
注解是byType
注入了。@Autowired注解的原理就是AutowiredAnnotationBeanPostProcessor
这个后置处理器在背后默默付出,找出符合条件的属性并使用反射进行注入。 - 除此之外,咱们还学会了如何使用
InstantiationAwareBeanPostProcessor
后置处理器来破坏@Autowired
的功能。 - I am a slow walker, but I never walk backwards.
网友评论