文章目录
- 什么是注入模型
- 注入模型的种类
- 案例分析
- 例子一
- 创建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会对使用哪个构造方法来注入进行推断)。
思考
回到第二个结论,如果我们不改变注入模型,直接去掉默认的构造方法,那么是什么结果呢?