文章目录
- 什么是注入模型
- 注入模型的种类
- 案例分析
- 例子一
- 创建Bean对象
- 创建配置类
- 重写后置处理器
- 创建测试类
- 执行测试方法
- 分析
 
- 例子二
- 改写站点类,去掉@Autowired注解
- 重写后置处理器
- 执行测试方法
- 分析
- 思考
 
 
✨这里是第七人格的博客✨小七,欢迎您的到来~✨
🍅系列专栏:【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个结论
-  Spring默认的注入模型是0,符合我们在AbstractBeanDefinition看到的结果 
-  注入模型是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会对使用哪个构造方法来注入进行推断)。
思考
回到第二个结论,如果我们不改变注入模型,直接去掉默认的构造方法,那么是什么结果呢?



















