> 技术文档 > spring源码学习 五

spring源码学习 五


spring源码学习 五

    • 前言
    • 一、Aware和InitializingBean接口
    • 二、各种初始化与销毁方法的执行顺序
    • 三、总结

前言

这一篇文章,我们来认识AwareInitializingBean接口,了解它们是做什么的以及它们和Spring常用注解有什么关系。此外还需要了解各种初始化和销毁方法的执行顺序。

一、Aware和InitializingBean接口

Aware接口是干嘛的?

简单来说,它们是用来注入一些与容器相关的信息,例如:

  1. BeanNameAware 注入 bean 的名字
  2. BeanFactoryAware 注入 BeanFactory 容器
  3. ApplicationContextAware 注入 ApplicationContext 容器
  4. 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(); }}

输出结果:

spring源码学习 五

没问题,回调了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+\" 我在初始化\"); } }

再次执行看看效果:

spring源码学习 五

没有问题。

有同学就问了,@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工厂后处理器来解析注解。

到这里一切都很正常,结果:

spring源码学习 五

现在我们往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源码学习 五

不出所料,失效了…

还注意Spring输出了一条信息:

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

结果:

spring源码学习 五

执行成功。

二、各种初始化与销毁方法的执行顺序

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

结果:

spring源码学习 五

对于销毁方法,执行顺序是类似的:

  • 使用@PreDestory标记的销毁方法
  • DisposableBean接口的实现方法
  • @Bean的destroyMethod属性指定的方法

三、总结

  1. @Autowired@PostConstruct等功能属于扩展功能,需要Bean后处理器提供支持,而AwareInitializingBean接口的方法不需要,属于内置功能,Spring容器总会回调它们,Spring里的许多组件类都选择去实现Aware和InitializingBean接口,而不是注解,因为这种扩展功能有可能失效。

  2. 各种初始化和销毁方法的执行顺序:

  • 扩展功能(@PostConstruct@PreDestroy

  • 内置接口(InitializingBeanDisposableBean

  • @Bean指定