面试中碰到面试官问:”Spring 注解是如果工作的?“,当前我一惊,完了这不触及到我的知识误区了吗?,还好我机智,灵机一动回了句:Spring 注解的工作流程倒还没有看到,但是我知道@Autowired注解的工作流程,后面不用说了一顿巴拉,面试官都连连点头。

面试中要活用转移话题,要避免回答 ”不知道“,要引导面试官掉入你擅长的技术,然后才有机会教他作人。

@Autowired 相关的类

@Autowired 注解的主要功能就是完成自动注入,使用也非常简单(Spring都安排好了),但是要想知道 @Autowired 注解的内部现实,就需要看一下Spring源码了。接下来一步步的解剖 @Autowired 的实现原理,首先理一下与 @Autowired 注解相关的类,然后一步步的跟踪源码,直到理解 @Autowired 的原理。

AutowiredAnnotationBeanPostProcessor 类

AutowiredAnnotationBeanPostProcessor是实现 @Autowired 功能的主要类,它有一些方法是会用解析 @Autowired 注解并实现自动注入的功能,下面是它的继承图:

从上图可以发现 AutowiredAnnotationBeanPostProcessor 最上层是 BeanPostProcessor 是一个后处理器,当然除了后处理器外中间还有InstantiationAwareBeanPostProcessorMergedBeanDefinitionPostProcessor

InstantiationAwareBeanPostProcessor 接口

postProcessBeforeInstantiation方法

在Bean实例化之前调用,可以返回一个Bean实例,默认返回null

  1. @Nullabledefault Object postProcessBeforeInstantiation(Class<?> beanClass, String
  2. beanName) throws BeansException { return null;}

postProcessAfterInstantiation方法

在Bean创建完成后,设置属性之前调用。

  1. default boolean postProcessAfterInstantiation(Object bean, String
  2. beanName) throws BeansException { return true;}

postProcessProperties方法

  1. @Nullable default PropertyValues postProcessProperties(PropertyValues pvs, Object
  2. bean, String beanName) throws BeansException { return null;}

Bean创建完后,设置属性之前调用

先记住 InstantiationAwareBeanPostProcessor 接口,后面会跟踪调用它的地方,就很容易理解了。

MergedBeanDefinitionPostProcessor

MergedBeanDefinitionPostProcessor 也是一个继承 BeanPostProcessor 接口的后处理器,它的主要作用就是可以处理操作BeanDefinition对象,由于Bean的实例化是通过 BeanDefinition 的, 通过操作BeanDefinition ,这样可以使Bean的实例化时发生一些变化。

MergedBeanDefinitionPostProcessor 只有两个方法

postProcessMergedBeanDefinition方法

  1. void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,
  2. Class<?> beanType, String beanName);

Bean的BeanDefinition 被合并后调用此方法。

resetBeanDefinition

  1. default void resetBeanDefinition(String beanName) {}

当一个BeanDefinition被重置后调用 。

AutowireCapableBeanFactory 接口

AutowireCapableBeanFactory继承自BeanFactory,除了提供基础的Bean操作外,从接口的名字就可以推断出的它还有自动注入的能力。AutowireCapableBeanFactory 提供四种注入模型:

  • AUTOWIRE_NO: 没有显示的定义注入模型
  • AUTOWIRE_BY_NAME: 通过Bean名称注入
  • AUTOWIRE_BY_TYPE: 通过Bean的类型注入
  • AUTOWIRE_CONSTRUCTOR:通过Bean的构造方法注入

AutowireCapableBeanFactory 接口有不少方法,但大部分都是跟自动注入的相关。@Autowired 的主要功能就是在Bean实例化后,为其设置属性,所以在 AutowireCapableBeanFactory 接口有一个 createBean方法,用于创建Bean并设置Bean的属性:

  1. <T> T createBean(Class<T> beanClass) throws BeansException;

createBean方法,它的调用时机是创建Bean的时候,稍后会说到它的调用时机。

AbstractAutowireCapableBeanFactory

AbstractAutowireCapableBeanFactory 继承 AbstractBeanFactory并实现了AutowireCapableBeanFactory接口,所以它也实现了AutowireCapableBeanFactory中的createBean方法。

  1. public <T> T createBean(Class<T> beanClass) throws BeansException {
  2. // Use prototype bean definition, to avoid registering bean as dependent bean.
  3. RootBeanDefinition bd = new RootBeanDefinition(beanClass);
  4. bd.setScope(SCOPE_PROTOTYPE);
  5. bd.allowCaching = ClassUtils.isCacheSafe(beanClass, getBeanClassLoader());
  6. return (T) createBean(beanClass.getName(), bd, null);}

Bean创建的生命周期

通过了解Bean创建的生命周期,才可以将上面与 @Autowired 相关的类串起来,首先这里不会过多的介绍Bean的创建细节,只关注自动注入相关的代码。

Bean的创建过程

Spring中默认的Bean都是懒加载的,所以一个Bean的创建会从调用getBean方法开始,如果不考虑缓存、上层容器的情况,Bean的创建会经过以下方法:

  • getBean:BeanFactory的方法,获取Bean实例
  • doGetBean:获取Bean的实例,获取顺序依次为:单例池、父容器,如果从以上2种途径都没获取到Bean实例就会创建新的
  • createBean:创建 Bean,这里的createBean,跟上面介绍的是一回事
  • doCreateBean:创建Bean实例
  • populateBean:设置Bean属性

以上流程中的getBeandoGetBean不多作说明了, 重点关注createBean前面提到AbstractAutowireCapableBeanFactory.createBean方法,所以说你在调用getBean方法获取Bean的实例时,如果这个Bean实例还没有被创建,那么createBean就会被调用。

通过简单的说明Bean创建的生命周期,就能找到 @Autowired 注解实现的入口,接下来再继续跟踪createBean方法。

收集注入元信息

收集注入元信息的步骤的,其实就是调用AutowiredAnnotationBeanPostProcessor类方法来实现的。

Bean 创建之前

以下是createBean方法,在Bean创建之前调用postProcessBeforeInstantiation的地方。为是阅读方便省略了一些代码,大致的流程就是:

  • 首先调用 resolveBeforeInstantiation 方法,执行 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation方法
  • 如果postProcessBeforeInstantiation返回Bean实例那么直接返回这个实例,如果返回nul 继续调用doCreateBean
  1. @Override
  2. protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
  3. throws BeanCreationException {
  4. ...
  5. try {
  6. // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
  7. Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
  8. if (bean != null) {
  9. return bean;
  10. }
  11. }
  12. catch (Throwable ex) {
  13. ...
  14. }
  15. ...
  16. Object beanInstance = doCreateBean(beanName, mbdToUse, args);
  17. ...
  18. ...
  19. }
  20. @Nullable
  21. protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
  22. Object bean = null;
  23. if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
  24. // Make sure bean class is actually resolved at this point.
  25. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
  26. Class<?> targetType = determineTargetType(beanName, mbd);
  27. if (targetType != null) {
  28. bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
  29. if (bean != null) {
  30. bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
  31. }
  32. }
  33. }
  34. mbd.beforeInstantiationResolved = (bean != null);
  35. }
  36. return bean;
  37. }

这里 AutowiredAnnotationBeanPostProcessor 的 postProcessBeforeInstantiation 的方法会被调用,由于AutowiredAnnotationBeanPostProcessor 并没有重写这个方法,所以什么都不做。

操作 BeanDefinition

上面说过 postProcessBeforeInstantiation 方法返回 null 的话会继续执行doCreateBean方法:

  1. protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
  2. throws BeanCreationException {
  3. // Allow post-processors to modify the merged bean definition.
  4. synchronized (mbd.postProcessingLock) {
  5. if (!mbd.postProcessed) {
  6. try {
  7. applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
  8. }
  9. catch (Throwable ex) {
  10. throw new BeanCreationException(mbd.getResourceDescription(), beanName,
  11. "Post-processing of merged bean definition failed", ex);
  12. }
  13. mbd.postProcessed = true;
  14. }
  15. }
  16. ...
  17. populateBean(beanName, mbd, instanceWrapper);
  18. ...

在 doCreateBean 方法中,会调用调用applyMergedBeanDefinitionPostProcessors方法:

  1. protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
  2. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  3. if (bp instanceof MergedBeanDefinitionPostProcessor) {
  4. MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
  5. bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
  6. }
  7. }

MergedBeanDefinitionPostProcessor接口上面提到到的,AutowiredAnnotationBeanPostProcessor 实现了这个接口所以直接进入到 AutowiredAnnotationBeanPostProcessor 中的 postProcessMergedBeanDefinition方法:


  1. @Override
  2. public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
  3. InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
  4. metadata.checkConfigMembers(beanDefinition);
  5. }
查找注入元数据

接着继续进入到findAutowiringMetadata,findAutowiringMetadata 会调用buildAutowiringMetadata方法创建注入元数据,然后将元数据缓存到injectionMetadataCache属性中:


  1. private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
  2. // Fall back to class name as cache key, for backwards compatibility with custom callers.
  3. String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
  4. // Quick check on the concurrent map first, with minimal locking.
  5. InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
  6. if (InjectionMetadata.needsRefresh(metadata, clazz)) {
  7. synchronized (this.injectionMetadataCache) {
  8. metadata = this.injectionMetadataCache.get(cacheKey);
  9. if (InjectionMetadata.needsRefresh(metadata, clazz)) {
  10. ...
  11. metadata = buildAutowiringMetadata(clazz);
  12. this.injectionMetadataCache.put(cacheKey, metadata);
  13. }
  14. }
  15. }
  16. return metadata;
  17. }
创建注入元数据

仔细查看buildAutowiringMetadata方法的实现,它会反射类的方法和属性,同时还会向上查找父类,然后生成InjectionMetadata

  1. private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
  2. if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
  3. return InjectionMetadata.EMPTY;
  4. }
  5. List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
  6. Class<?> targetClass = clazz;
  7. do {
  8. final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
  9. ReflectionUtils.doWithLocalFields(targetClass, field -> {
  10. MergedAnnotation<?> ann = findAutowiredAnnotation(field);
  11. if (ann != null) {
  12. if (Modifier.isStatic(field.getModifiers())) {
  13. if (logger.isInfoEnabled()) {
  14. logger.info("Autowired annotation is not supported on static fields: " + field);
  15. }
  16. return;
  17. }
  18. boolean required = determineRequiredStatus(ann);
  19. currElements.add(new AutowiredFieldElement(field, required));
  20. }
  21. });
  22. ReflectionUtils.doWithLocalMethods(targetClass, method -> {
  23. Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
  24. if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
  25. return;
  26. }
  27. MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
  28. if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
  29. if (Modifier.isStatic(method.getModifiers())) {
  30. if (logger.isInfoEnabled()) {
  31. logger.info("Autowired annotation is not supported on static methods: " + method);
  32. }
  33. return;
  34. }
  35. if (method.getParameterCount() == 0) {
  36. if (logger.isInfoEnabled()) {
  37. logger.info("Autowired annotation should only be used on methods with parameters: " +
  38. method);
  39. }
  40. }
  41. boolean required = determineRequiredStatus(ann);
  42. PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
  43. currElements.add(new AutowiredMethodElement(method, required, pd));
  44. }
  45. });
  46. elements.addAll(0, currElements);
  47. targetClass = targetClass.getSuperclass();
  48. }
  49. while (targetClass != null && targetClass != Object.class);
  50. return InjectionMetadata.forElements(elements, clazz);
  51. }

小结

收集注入元数据过程,首先调用AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition方法,然后调用findAutowiringMetadata方法查找元数据,如果找到相应类的注入元数据 ,就会调用buildAutowiringMetadata方法创建InjectionMetadata,最后将新创建的注入元数据保存在injectionMetadataCache缓存起来。

设置Bean属性

收信完注入元数据后,Bean的属性还是没有注入的,还需要将执行属性注入。还是在doCreateBean方法中,收集完注入元数据后,紧接着会调用populateBean

  1. protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
  2. boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
  3. boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
  4. PropertyDescriptor[] filteredPds = null;
  5. if (hasInstAwareBpps) {
  6. if (pvs == null) {
  7. pvs = mbd.getPropertyValues();
  8. }
  9. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  10. if (bp instanceof InstantiationAwareBeanPostProcessor) {
  11. InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
  12. PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
  13. if (pvsToUse == null) {
  14. if (filteredPds == null) {
  15. filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
  16. }
  17. pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
  18. if (pvsToUse == null) {
  19. return;
  20. }
  21. }
  22. pvs = pvsToUse;
  23. }
  24. }
  25. }
  26. }

可以看到在populateBean中会调用InstantiationAwareBeanPostProcessor.postProcessProperties方法,由于已经知道 AutowiredAnnotationBeanPostProcessor 是实现 InstantiationAwareBeanPostProcessor 的,所以可以直接查看实现方法:

  1. @Override
  2. public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
  3. InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
  4. try {
  5. metadata.inject(bean, beanName, pvs);
  6. }
  7. catch (Throwable ex) {
  8. throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
  9. }
  10. return pvs;
  11. }

postProcessProperties就很简单的,查找 InjectMetadata,然后调用 InjectMetadata.inject方法。到这里其实就已经知道@Autowire的实现机制了,接下来就是根据InjectionMetadata中的信息实现属性注入了。

如果需要深入研究的话,有兴趣的还可以继续往下看。

总结

本文大致讲解了 @Autowire 相关的类与实现的机制,@Autowire 注解的实现主要是理解AutowiredAnnotationBeanPostProcessor类,还有收集注入元数据、设置注入属性的调用时机。

通过查看AutowiredAnnotationBeanPostProcessor类源码,相信你也可以自定义注入功能。

本人知识水平有限,如有错误,谢谢大家指正。

欢迎关注我的公众号:架构文摘,获得独家整理120G的免费学习资源助力你的架构师学习之路!

公众号后台回复arch028获取资料:

Spring @Autowired 注解自动注入流程是怎么样?的更多相关文章

  1. Spring学习(六)-----Spring使用@Autowired注解自动装配

    Spring使用@Autowired注解自动装配 在上一篇 Spring学习(三)-----Spring自动装配Beans示例中,它会匹配当前Spring容器任何bean的属性自动装配.在大多数情况下 ...

  2. 解决Spring+Quartz无法自动注入bean问题

    问题 我们有时需要执行一些定时任务(如数据批处理),比较常用的技术框架有Spring + Quartz中.无奈此方式有个问题:Spring Bean无法自动注入. 环境:Spring3.2.2 + Q ...

  3. Spring @Autowired 注解 学习资料

    Spring @Autowired 注解 学习资料 网址 Spring @Autowired 注解 https://wiki.jikexueyuan.com/project/spring/annota ...

  4. Spring@Autowired注解与自动装配

    1   配置文件的方法 我们编写spring 框架的代码时候.一直遵循是这样一个规则:所有在spring中注入的bean 都建议定义成私有的域变量.并且要配套写上 get 和 set方法. Boss ...

  5. 【转】Spring@Autowired注解与自动装配

    1   配置文件的方法 我们编写spring 框架的代码时候.一直遵循是这样一个规则:所有在spring中注入的bean 都建议定义成私有的域变量.并且要配套写上 get 和 set方法. Boss ...

  6. Spring Boot @Autowired 没法自动注入的问题

    Application 启动类: @SpringBootApplication @EnableConfigurationProperties @ComponentScan(basePackages = ...

  7. Spring@Autowired注解与自动装配(转发)

    1   配置文件的方法 我们编写spring 框架的代码时候.一直遵循是这样一个规则:所有在spring中注入的bean 都建议定义成私有的域变量.并且要配套写上 get 和 set方法. Boss ...

  8. [转] Spring@Autowired注解与自动装配

    1   配置文件的方法 我们编写spring 框架的代码时候.一直遵循是这样一个规则:所有在spring中注入的bean 都建议定义成私有的域变量.并且要配套写上 get 和 set方法. Boss ...

  9. Spring基于注解自动装配

    前面我们介绍Spring IoC装载的时候,使用XML配置这种方法来装配Bean,这种方法可以很直观的看到每个Bean的依赖,但缺点也很明显:写起来非常繁琐,每增加一个组件,就必须把新的Bean配置到 ...

随机推荐

  1. 【应用服务 App Service】App Service 中部署Java应用中文乱码现象

    问题情形 有时候部署在 Azure  App Service的 Java应用会出现乱码 详细日志 无 问题原因 因为 App Service默认的编码为gbk,所以在显示页面或传递中文字符时就会出现乱 ...

  2. MASM入门 (一)DOSBox的安装和使用

    目录 (1)DOSBox的下载安装 (2)DOSBox的使用 (3)Tips (1)DOSBox的下载安装 DOSBox从安装到使用的过程还是有些繁琐的,所以小编直接附上免安装版本,大家下载解压后点击 ...

  3. MVC查询

    前言 最近没什么好写的,所以写个查询来巩固一下知识 HTML @{ Layout = null; } <!DOCTYPE html> <html> <head> & ...

  4. Java安全之Commons Collections2分析

    Java安全之Commons Collections2分析 首发:Java安全之Commons Collections2分析 0x00 前言 前面分析了CC1的利用链,但是发现在CC1的利用链中是有版 ...

  5. 2020-2021-1 20209306 《linux内核原理与分析》第二周作业

    一.实验一内容及分析 1.实验一内容过程截图 2.实验一完成后收获 可以看到汇编代码中出现了eax.esp.ebp.eax是累加寄存器,esp是堆栈指针寄存器,ebp是基指针寄存器.汇编代码中用到了m ...

  6. 使用contentProvider

    内部利用contentProvider暴露接口供外部查询删除操作,外部查询删除使用contentResolver,首先使用sqlite创建一个数据库表student,然后使用contentProvid ...

  7. python基础三:函数

    def name(参数1,参数2,参数3,...) 可以自定义一些自己需要的函数来简化自己的工作. 如:自定义一个计算函数 def  mycount(a,b,c): y=a+b-c return y ...

  8. 配置交换机之间直连链路聚合-LACP模式

    组网图形 LACP模式链路聚合简介 以太网链路聚合是指将多条以太网物理链路捆绑在一起成为一条逻辑链路,从而实现增加链路带宽的目的.链路聚合分为手工模式和LACP模式. LACP模式需要有链路聚合控制协 ...

  9. 应该怎么提升4G工业路由器的无线信号?

    4G工业路由器如今应用的范围非常的广泛,在实际使用中也遇到了很多的问题,其中经常被问到的一个问题就是我们怎么保证4G工业路由器的良好信号强度.在互联网上也有很多关于如何找到最佳信号的方法,但对于固定和 ...

  10. Java学习的第三十二天

    1. 2.综合例题没太看懂 3.明天复习第十二章