美文网首页
都0202年了还在说spring@Autowired是byTyp

都0202年了还在说spring@Autowired是byTyp

作者: avengerEug | 来源:发表于2021-03-25 11:10 被阅读0次

前言

一、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()方法
  • 或者直接在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方法,继续调试代码,如下图所示:
    在这里插入图片描述
    此时我们跟着断点走,进入到AutowiredAnnotationBeanPostProcessorpostProcessPropertyValues方法,如下图所示:
    在这里插入图片描述
    我们继续执行,将断点打到方法的出口处,如下图所示:
    在这里插入图片描述
    综上所述,@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.

相关文章

  • 都0202年了还在说spring@Autowired是byTyp

    前言 基于上篇文章从源码的角度查找Spring @Autowired注解不能依赖注入静态变量的原因[https:/...

  • Spring的@AutoWired注释

    Spring@Autowired最终是根据类型来查找和装配元素的。 @Autowired 注释通过执行“byTyp...

  • 你都20了  还在说害怕?

    慢慢发现自己对友情都好像有占有欲一样。还是说其实是害怕被人抛弃的感觉。大学又开学了,我虽然有蛮多的计划要考什么什么...

  • 都在说<<都挺好>>

    一身硬气的苏明玉,心软,在面对自私的家人这仗,输了。 为什么说是场仗? 很简单,苏明玉跟她的家人从来都是对峙的关系...

  • Spring@Autowired注入顺序

    在使用@Bean注解的时候,发现@Autowired注入的对象一直是NULL,然后换一个类代码相同@Autowir...

  • 这被窝就是冷啊

    好冷,今天好冷,整个人都冷,漏风了吗,不对,噎好了,谁在说话,是你吗?还是你?或者是哦了?你在说啥?噢噢⊙_⊙,是...

  • 你都多大了还单身

    文/俞文 01 前几天朋友圈看到有朋友转发93年的小视频,点开后便进入了抖音,看见了频刷不绝的“93年火了”的小视...

  • 原创【时雨】

    今春 是一坛陈酿 我醉了 浑身都染满了它的酒气 是我醉了 真的醉了 人们都好像是在说: 干……干……干…… 不过还...

  • 快离职了,请别谈理想

    工作这么久,你有对某人某事失望过吗?都选择离职了,你还一直在说公司多么好,跟我谈理想, 我连最基本的保障都...

  • 今天听到邻居都在议论一个家暴妻子的人。大家都在说:“都这把年纪了,两个人能好好的一天是一天。怎么还动手呢!?”每个...

网友评论

      本文标题:都0202年了还在说spring@Autowired是byTyp

      本文链接:https://www.haomeiwen.com/subject/ppulhltx.html