• 【Spring源码解析】一文读懂Spring注入模型:开发者必备知识


    ✨这里是第七人格的博客✨小七,欢迎您的到来~✨

    🍅系列专栏:【Spring源码解析】🍅

    ✈️本篇内容: 浅谈Spring注入模型✈️

    🍱本篇收录完整代码地址:https://gitee.com/diqirenge/spring-book/tree/master/injection-model🍱

    什么是注入模型

    首先我们要明确,注入模型和注入Bean的方式不能混为一谈。从Spring的官网我们可以知道,Spring注入bean的方式有两种,一种是通过构造方法进行注入,另外一种是通过setter方法进行注入。说简单一点就是注入Bean的方式是一种注入bean的策略,而注入模型是Bean的一种属性(其实是BeanDefinition的属性),他会影响bean的一些行为。

    注入模型的种类

    Spring的注入模型一共有四种,作为静态常量定义在AbstractBeanDefinition类当中

    /**
     * Constant that indicates no external autowiring at all.
     * 手动注入,这也是默认的注入方式
     *
     * @see #setAutowireMode
     */
    public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
    
    /**
     * Constant that indicates autowiring bean properties by name.
     * 按名字自动注入
     *
     * @see #setAutowireMode
     */
    public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
    
    /**
     * Constant that indicates autowiring bean properties by type.
     * 按类型自动注入
     *
     * @see #setAutowireMode
     */
    public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
    
    /**
     * Constant that indicates autowiring a constructor.
     * 按构造方法自动注入
     *
     * @see #setAutowireMode
     */
    public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    案例分析

    想要获取一个Spring管理的对象,我们不少人会采用在属性上加@Autowired或者@Resource来实现,接下来,我们就先写一个使用@Autowired的最常见的例子。

    例子一

    创建Bean对象

    地址

    @Component
    @Slf4j
    public class Address {
     
        public void info(){
            log.info("小七个人博客地址:www.52javaee.com");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    站点

    @Component
    @Slf4j
    public class Website {
        @Autowired
        private Address address;
     
        public Website(){
            log.info("执行了默认的无参的构造方法");
        }
     
        public Website(Address address){
            log.info("执行了有参的构造方法{}", address);
            this.address = address;
        }
     
        public void setAddress(Address address){
            log.info("执行了set方法{}", address);
            this.address = address;
        }
     
        public void showAddress(){
            this.address.info();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    创建配置类

    @ComponentScan("org.example.model.case1")
    public class MyBeanConfig {
    }
    
    • 1
    • 2
    • 3

    重写后置处理器

    @Slf4j
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) beanFactory.getBeanDefinition("website");
            log.info("Spring注入模型[mode]:{}",beanDefinition.getAutowireMode());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    为什么要重写后置处理器呢?因为我们可以通过这个方式,获取website这个beanDefinition在Spring容器中对应的autowireMode的值,并且可以修改这个autowireMode的值,来观察下注入模型的改变,对bean的注入方式有什么影响。

    创建测试类

    public class Main {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
            // 向上下文注册MyBeanConfig类,并且注册MyBeanFactoryPostProcessor类
            context.register(MyBeanConfig.class);
            context.register(MyBeanFactoryPostProcessor.class);
            // 刷新上下文
            context.refresh();
            // 获取Website实例
            Website website = context.getBean(Website.class);
            // 打印Website实例的地址
            website.showAddress();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    执行测试方法

    请添加图片描述

    分析

    通过执行结果我们可以知道,在属性上面加@Autowired的方式,使用的是Spring默认的注入模型——手动注入。

    例子二

    改写站点类,去掉@Autowired注解

    @Component
    @Slf4j
    public class Website {
        private Address address;
     
        public Website(){
            log.info("执行了默认的无参的构造方法");
        }
     
        public Website(Address address){
            log.info("执行了有参的构造方法{}", address);
            this.address = address;
        }
     
        public void setAddress(Address address){
            log.info("执行了set方法{}", address);
            this.address = address;
        }
     
        public void showAddress(){
            this.address.info();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    重写后置处理器

    @Slf4j
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) beanFactory.getBeanDefinition("website");
    //        // case2-1:默认不使用自动注入
    //        beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_NO);
    
    //        // case2-2:使用自动注入(按名称/按类型)
    //        beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
    //        beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    
    //        // case2-3:使用自动注入(按构造方法)
    //        beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
            log.info("Spring注入模型[mode]:{}",beanDefinition.getAutowireMode());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    暂时什么都不改

    执行测试方法

    请添加图片描述

    分析

    观察以上结果,我们可以大胆的得出2个结论

    1. Spring默认的注入模型是0,符合我们在AbstractBeanDefinition看到的结果

    2. 注入模型是0,意味着执行默认的构造方法,并且不会执行set方法,所以我们的address对象是空的,因此

      this.address.info();

    这行代码就抛出了空指针异常。

    针对第一个结论,我们改变一下注入模型,再执行测试方法看看结果

    1.1 在后置处理器添加以下代码:

     beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
    
    • 1

    执行结果正常了,并且调用了我们的set方法

    在这里插入图片描述

    1.2 再次修改注入模型为AUTOWIRE_BY_TYPE

    beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    
    • 1

    执行结果如下,也调用了set方法

    在这里插入图片描述

    1.3 再次修改注入模型为AUTOWIRE_CONSTRUCTOR

    beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
    
    • 1

    执行结果如下

    在这里插入图片描述

    不再调用set方法,而且跳过了无参构造方法,执行了有参构造方法,说明在注入模型为3时,Spring自动帮我们选择了正确的构造方法来注入(也就是说Spring会对使用哪个构造方法来注入进行推断)。

    思考

    回到第二个结论,如果我们不改变注入模型,直接去掉默认的构造方法,那么是什么结果呢?

  • 相关阅读:
    SQL Server 2019 安装
    [NOI2022] 众数 题解
    VMware使用教程
    牛客周赛 Round 15_B
    Flutter:计数器应用分析
    申请免费通配符证书
    Qt之OpenSSL
    【Java基础】成员变量和局部变量及封装
    gitignore文件的语法规则
    orm查询优化
  • 原文地址:https://blog.csdn.net/a18602320276/article/details/132859808