【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;

案例分析

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

例子一

创建Bean对象

地址

@Component
@Slf4j
public class Address {
 
    public void info(){
        log.info("小七个人博客地址:www.52javaee.com");
    }
}

站点

@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();
    }
}

创建配置类

@ComponentScan("org.example.model.case1")
public class MyBeanConfig {
}

重写后置处理器

@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());
    }
}

为什么要重写后置处理器呢?因为我们可以通过这个方式,获取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();
    }
}

执行测试方法

请添加图片描述

分析

通过执行结果我们可以知道,在属性上面加@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();
    }
}

重写后置处理器

@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());
    }
}

暂时什么都不改

执行测试方法

请添加图片描述

分析

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

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

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

    this.address.info();

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

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

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

 beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);

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

在这里插入图片描述

1.2 再次修改注入模型为AUTOWIRE_BY_TYPE

beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

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

在这里插入图片描述

1.3 再次修改注入模型为AUTOWIRE_CONSTRUCTOR

beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);

执行结果如下

在这里插入图片描述

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

思考

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

第七人格

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值