spring源码学习 五
spring源码学习 五
前言
这一篇文章,我们来认识Aware
和InitializingBean
接口,了解它们是做什么的以及它们和Spring常用注解有什么关系。此外还需要了解各种初始化和销毁方法的执行顺序。
一、Aware和InitializingBean接口
Aware接口是干嘛的?
简单来说,它们是用来注入一些与容器相关的信息,例如:
BeanNameAware
注入 bean 的名字BeanFactoryAware
注入 BeanFactory 容器ApplicationContextAware
注入 ApplicationContext 容器EmbeddedValueResolverAware
注入 ${}
(如果对上面的一些名词不熟悉的同学可以翻看前几篇文章~)
InitializingBean
接口呢?是提供初始化方法的接口。
现在我们来看看到底是怎么个效果。
创建一个类叫MyBean,去实现BeanNameAware,当容器在创建这个Bean后,初始化之前就会调用接口的方法,把Bean的名字通过参数传过来。
public class MyBean implements BeanNameAware { @Override public void setBeanName(String name) { System.out.println(this+\" 我的名字是:\"+name); }}
现在我们就来准备一个干净的容器,把它放到里边,给它随便起个名(第一个参数)。
public class AwareTest { public static void main(String[] args) { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean(\"啦啦啦啦\",MyBean.class); context.refresh(); }}
输出结果:
没问题,回调了Bean名字接口。
现在我们让MyBean去实现ApplicationContextAware
接口和InitializingBean
接口,前者是传递Application容器,后者是提供初始化方法。
执行的顺序是先去回调Aware接口,然后才是InitializingBean里的方法。
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean { @Override public void setBeanName(String name) { System.out.println(this+\" 我的名字是:\"+name); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println(this+\" 容器是:\"+applicationContext); } @Override public void afterPropertiesSet() throws Exception { System.out.println(this+\" 我在初始化\"); } }
再次执行看看效果:
没有问题。
有同学就问了,@Autowired
不是也可以注入ApplicationContext容器嘛,还有@PostConstruct
不是也可以标注初始化方法嘛,为什么还要上边的接口呢?
- 大家还记得前面说到,@Autowired的解析需要Bean后处理器吗?它属于是扩展功能,ApplicationContextAware的方法执行可不需要这个Bean后处理器来支撑。
- Aware接口属于内置功能,不需要任何扩展,Spring就可以识别!
- 另外,扩展的功能有可能失效,但是内置功能不会。
我们现在就来看看@Autowired失效的例子。
首先我们先创建一个配置类Config1:
@Configurationpublic class Config1 { @Autowired public void setApplicationContext(ApplicationContext applicationContext){ System.out.println(\"@Autowired注入 ApplicationContext\"); } @PostConstruct public void init(){ System.out.println(\"@PostConstruct 初始化\"); }}
里面通过@Autowired 注入了一个ApplicationContext容器,还有一个@PostConstruct标记的初始化方法,现在我们把它加入到Bean容器里。
public static void main(String[] args) { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean(\"myConfig1\",Config1.class); context.registerBean(AutowiredAnnotationBeanPostProcessor.class);//Bean后处理器,可解析@Autowire context.registerBean(CommonAnnotationBeanPostProcessor.class);//Bean后处理器,可解析@PostConstruct context.registerBean(ConfigurationClassPostProcessor.class);//Bean工厂后处理器,可解析@Configuration、@Bean context.refresh();}
加入Bean的同时也往干净的容器里加入一些Bean后处理器和Bean工厂后处理器来解析注解。
到这里一切都很正常,结果:
现在我们往Config1里加点料:
@Configurationpublic class Config1 { @Autowired public void setApplicationContext(ApplicationContext applicationContext){ System.out.println(\"@Autowired注入 ApplicationContext\"); } @PostConstruct public void init(){ System.out.println(\"@PostConstruct 初始化\"); } @Bean //Bean工厂后处理器 public BeanFactoryPostProcessor processor1(){ return new BeanFactoryPostProcessor() { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println(\"执行 processor1()\"); } }; }}
可以看到我们用@Bean创建一个BeanFactoryPostProcessor的实现processor1出来,现在我们再看看是否还能正常执行@Autowired和@PostConstruct标记的方法:
不出所料,失效了…
还注意Spring输出了一条信息:
This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method’s declaring @Configuration class.
关键点如上所示,提示我们这些注解会失效。
这里我们从容器的refresh方法说起
-
这个方法首先会找到BeanFactory后处理器并执行,目的是补充Bean定义
-
然后会添加Bean后处理器,给Bean的各个生命周期提供扩展功能
-
随后创建所有单例Bean(创建、依赖注入、初始化)
-
执行Aware及InitializingBean接口方法
-
Bean创建成功
为什么我们在配置类用@Bean创建一个BeanFactoryPostProcessor的实现processor1就会失效?
我们看refresh的第一步看起,找到BeanFactory后处理器并执行,说明需要用到我们用@Bean添加的BeanFactoryPostProcessor
,这时就会提前把Config1
创建出来,才能用上它。注意,配置类也是单例Bean,这样一来,执行的顺序就被改变了。
执行步骤第三点被提前到第一点,配置Bean被提前创建出来,但此时并没有添加Bean后处理器去解析@Autowired和@PostConstruct,所以这种方式的依赖注入和初始化就失效了。顺序改变成:
-
随后创建所有单例Bean(创建、依赖注入【无效】、初始化【无效】)
-
执行Aware及InitializingBean接口方法
-
Bean创建成功
-
这个方法首先会找到BeanFactory后处理器并执行,目的是补充Bean定义
-
然后会添加Bean后处理器,给Bean的各个生命周期提供扩展功能
接下来我们把@Autowired和@PostConstruct改成去实现Aware和InitializingBean接口方法,看看会怎么样。
@Configurationpublic class Config2 implements InitializingBean, ApplicationContextAware { @Override public void afterPropertiesSet() throws Exception { System.out.println(\"执行初始化\"); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println(\"注入 ApplicationContext\"); } @Bean public BeanFactoryPostProcessor processor2(){ return new BeanFactoryPostProcessor() { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println(\"执行 processor2\"); } }; }}
main函数:
public static void main(String[] args) { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean(\"myConfig2\",Config2.class); context.registerBean(AutowiredAnnotationBeanPostProcessor.class);//Bean后处理器,可解析@Autowire context.registerBean(CommonAnnotationBeanPostProcessor.class);//Bean后处理器,可解析@PostConstruct context.registerBean(ConfigurationClassPostProcessor.class);//Bean工厂后处理器,可解析@Configuration context.refresh();}
结果:
执行成功。
二、各种初始化与销毁方法的执行顺序
对于Bean的初始化,我们看以下三种情况:
- @Bean注解的属性标记初始化方法
@Bean(initMethod = \"init\")public Bean1 bean1(){ return new Bean1();}
- 实现InitializingBean接口方法
public class Bean1 implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { System.out.println(\"InitializingBean 初始化\"); }}
- 使用@PostConstruct
public class Bean1 implements InitializingBean { @PostConstruct public void init1(){ System.out.println(\"@PostConstruct 初始化\"); }}
如果把它们放在一起,都可以生效吗?如果是,谁先谁后呢?
@SpringBootApplicationpublic class AwareTest { public static void main(String[] args) { SpringApplication.run(AwareTest.class,args); } @Bean(initMethod = \"init\") public Bean1 bean1(){ return new Bean1(); }}
结果:
对于销毁方法,执行顺序是类似的:
- 使用
@PreDestory
标记的销毁方法 DisposableBean
接口的实现方法@Bean的destroyMethod
属性指定的方法
三、总结
-
@Autowired
、@PostConstruct
等功能属于扩展功能,需要Bean后处理器提供支持,而Aware
和InitializingBean
接口的方法不需要,属于内置功能,Spring容器总会回调它们,Spring里的许多组件类都选择去实现Aware和InitializingBean接口,而不是注解,因为这种扩展功能有可能失效。 -
各种初始化和销毁方法的执行顺序:
-
扩展功能(
@PostConstruct
、@PreDestroy
) -
内置接口(
InitializingBean
、DisposableBean
) -
@Bean指定