Spring bean的生命周期

当我们写一个很小的项目的时候,如果需要创建对象,那么很可能用new出来一个对象就行了。当项目越来越大的时候,对象的管理、对象之间的依赖关系、对象的生命周期管理就会变得越来越复杂。

如果可以不需要由来创建对象、管理对象的依赖关系、管理对象的生命周期的话,那就太好了。Spring框架就可以提供上述的功能,大大地提高了开发者的开发效率。

首先说一下对象创建,除了用new来创建一个对象之外,还可以使用反射来创建。(可以见我关于java反射的笔记)比如说,利用反射,只需要知道类名,就可以生成类的实例。以下示例代码就生成了一个MyService的实例。

Class<?> clazz = Class.forName("site.nemo.learn.reflect.MyService");
Object obj = clazz.newInstance();

既然可以用反射来创建实例,那么我只需要提供类名,框架就可以帮我来创建实例。那么我可以把我需要创建示例的类名都列在一个文件里面,框架只需要去读这个文件就可以了。

很早的时候,即还没有引入注解的时候,Spring框架是可以从xml文件里面生成对象实例的。

我们来举个例子。

新建一个maven项目,引入以下依赖:

	<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
    </dependencies>

src/main/java/site/nemo/entity包里面新建一个Project类:

public class Project {
}

src/main/resources文件夹里面新建一个bean.xml文件,内容如下。我们在bean.xml里面声明了一个<bean>,希望框架读取这个文件之后,能够帮我们创建一个Project类型的实例。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="project" class="site.nemo.entity.Project">
    </bean>

</beans>

那么,当框架读取了上面的xml文件之后,帮我们生成了实例,那么当我想要用这个实例的时候,怎么从框架拿呢?

  • 按名字
    • 也就是我们在上面的xml里面给bean设置的id
  • 按类型

下面演示一下从框架里面取出为我们生成好的bean:

我们在src/main/java/site/nemo/包里新建一个DemoApplication1

public class DemoApplication1 {

    public static void main(String[] args) {

        BeanFactory beanFactory = new ClassPathXmlApplicationContext("bean.xml");
        Project project = (Project) beanFactory.getBean("project");
        System.out.println(project);

        Project project2 = beanFactory.getBean(Project.class);
        System.out.println(project2);

        System.out.println(project == project2);
    }

}

打印结果为:

site.nemo.entity.Project@553f17c
site.nemo.entity.Project@553f17c
true

从打印结果可以看到,按名字取按类型取,取出来的是同一个实例。

那么就相当于有一个容器,它里面放着所有生成好的bean。那么我们可以假想一下,上面的那个ClassPathXmlApplicationContext它的逻辑是这样的:

  • 1.根据构造函数的参数即xml文件的路径,判断该文件是否存在
  • 2.解析该xml文件,判断xml里面的格式是否正确
  • 3.将所有<bean>声明解析之后存到两个个Map里面,一个Map是<name, Class<?>>
  • 4.xml文件解析完成之后,遍历第3步里面的Map,生成实例,存入另一个Map中<Class<?>, 实例>
  • 5.当用户调用getBean(xx)想要取出实例的时候,就从Map里面拿出来

一、如何取出bean

我们来看一下ClassPathXmlApplicationContext这个类,在IDEA里面从new ClassPathXmlApplicationContext("bean.xml");点进去看它的构造函数

ClassPathXmlApplicationContext构造函数

没什么特别的,我们可以知道xml配置文件路径是被存到了一个叫configLocations的字符串数组里面。

在点进去看下调用的内部的构造函数

ClassPathXmlApplicationContext构造函数-2

重点看下refresh方法,在IDEA里面点进去之后跳到了AbastractApplicationContext这个类的refresh里面去了。那么我们来看下AbastractApplicationContextClassPathXmlApplicationContext的关系

ClassPathXmlApplicationContext与父类的关系

可以看到AbastractApplicationContextClassPathXmlApplicationContext中间还隔着好几个xxxxxxApplicationContext,不过没关系,既然refresh直接跳到了AbastractApplicationContext这个类的refresh里面去了,说明中间的这几个类不看也罢。

把refresh方法的代码贴出来看下:

	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

大概就是以下几个步骤:

  • prepareRefresh() : 准备刷新前的一些前置条件,例如设置状态、验证propertysource、重新设置监听器等
  • obtainFreshBeanFactory() : 让子类刷新自己内部的bean factory
  • prepareBeanFactory :准备好此处要使用的bean factory,比如说设置bean的classloader、给beanfactory配置context回调、注册一些与环境有关的bean
  • postProcessBeanFactory : 进行一些beanfactory的后置处理
  • invokeBeanFactoryPostProcessors : 调用beanfactory的后置处理器
  • registerBeanPostProcessors : 注册bean的后置处理器
  • ……
  • onRefresh :让子类初始化一些特殊的bean
  • registerListeners :注册bean的监听器
  • finishBeanFactoryInitialization : 初始化所有的非懒初始化的单例bean
  • finishRefresh : 清除一些缓存、初始化并调用生命周期后置处理器、发布contextrefresh事件等

我们上面推理到,当调用getBean(xx)方法的时候,应该会从一个Map里面取出我们想要的bean。下面来找一下这个getBean(xx)方法。按照上图所示的继承关系,在AbstractApplicationContext里面找到了一系列getBean方法

getBean in AbstractApplicationContext

看里面的内容,实际上都是调用了getBeanFactory().getBean(xxx),那么我们可以找一下getBeanFactory()获得的是什么东西。AbstractApplicationContext本身的getBeanFactory()方法是个抽象方法,那么子类一定会实现该方法。

AbstractApplicationContext本身的getBeanFactory方法

我们还是看这个图,

ClassPathXmlApplicationContext与父类的关系

ClassPathXmlApplicationContext -> AbstractXmlApplicationContext -> AbstractRefreshableConfigApplicationContext -> AbstractRefreshableApplicationContext,最终在AbstractRefreshableApplicationContext里面找到了getBeanFactory()方法的实现

在里面找到了方法的实现

从上图看到,实际上是从DefaultListableBeanFactory取找我们想要的bean。看一下DefaultListableBeanFactory的层级关系

看一下的层级关系

我们最终在DefaultSingletonRegistry里面找到了getSingleton方法

getSingeton in DefaultSingletonRegistry

从上图的代码里面可以看到,会从一个叫singletonObjects的属性里面根据beanName取bean的实例。如果没取到,则从earlySingletonObjects里面去取

这个singletonObjectsearlySingletonObjects都是一个Map,是<beanName, beanInstance>的映射。

singletonObjects map

上面印证了我们的猜想,是从一个Map里面取出了bean。不过由于类的层级关系太过复杂,已经有点晕了。下面学习一个bean的生命周期。


二、bean的初始化与销毁

Spring框架帮我们生成了bean,不再需要我们自己手动new出来了。那么当我们想要在bean创建和销毁的时候做一些什么额外的操作,我们有什么办法呢?

比如说我有一个bean,我希望它初始化完成之后,能够让我知道,能够监听初始化完成。我希望监听到初始化完成之后,就立马去消费redis队列里面缓存的事件。同理,我也希望能够监听bean的销毁,当要销毁之前,我要做一些资源释放的工作。

那么我们可以有三种方法来监听初始化/销毁完成。

  • 方法一:使用@PostConstruct@PreDestroy注解
  • 方法二:baan实现InitializingBean、DisposableBean接口
  • 方法三:在使用@Bean注解的时候指定其initMethoddestroyMethod

下面分别演示一下:

新建一个maven项目,添加以下依赖:

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
    </dependencies>

方法一:使用@PostConstruct@PreDestroy注解

src/main/java/service里面新建一个MyService1,使用@PostConstruct@PreDestroy注解分别标注两个方法,希望分别在初始化之后、销毁之前被调用

@Component
public class MyService1 {

    @PostConstruct
    public void initFunc() {
        System.out.println("---MyService1 @PostConstruct function");
    }

    @PreDestroy
    public void destoryFunc() {
        System.out.println("---MyService1 @PreDestroy function");
    }
}

然后在src/main/java/configration里面新建一个配置类NemoConfiguration1

@Configuration
@ComponentScan(basePackages = {"service"})
public class NemoConfiguration1 {
}

然后在src/main/java里面新建一个NemoApplication1。主要是创建并初始化了一个IOC容器,然后再将IOC容器关闭。照理说IOC容器初始化了之后会自动创建bean,我们可以试验一下bean里面的@PostConstruct有没有被调用。然后IOC容器关闭的时候,会销毁所有bean,我们可以试验一下bean的@PreDestroy有没有被调用。

public class NemoApplication1 {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(NemoConfiguration1.class);
        System.out.println("----------IOC容器初始化完成----");

        System.out.println("------准备关闭IOC容器----");
        // 关闭IOC容器
        ctx.close();
        System.out.println("------IOC容器已关闭");
    }
}

可以看到控制台上打印了以下内容,可以看到使用@PostConstruct@PreDestroy注解的两个方法被调用了。

---MyService1 @PostConstruct function
----------IOC容器初始化完成----
----------准备关闭IOC容器----
---MyService1 @PreDestroy function
----------IOC容器已关闭

当普通的bean是用@PostConstruct的要注意:在@PostConstruct注解标注的方法里面,不能够抛出unchecked异常

方法二:baan实现InitializingBean、DisposableBean接口

我们在src/main/java/service2里面新建MyService2,它实现了InitializingBean, DisposableBean接口

@Component
public class MyService2 implements InitializingBean, DisposableBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("---MyService2 InitializingBean afterPropertiesSet function---");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("---MyService2 DisposableBean destroy function---");
    }
}

然后在src/main/java/configuration里面新建NemoConfiguration2

@Configuration
@ComponentScan(basePackages = {"service2"})
public class NemoConfiguration2 {
}

然后在src/main/java里面新建一个NemoApplication2

public class NemoApplication2 {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(NemoConfiguration2.class);
        System.out.println("----------IOC容器初始化完成----");

        System.out.println("------准备关闭IOC容器----");
        // 关闭IOC容器
        ctx.close();
        System.out.println("------IOC容器已关闭");
    }
}

可以看到控制台上打印了以下内容,可以看到InitializingBean, DisposableBean的两个方法被调用了。

---MyService2 InitializingBean afterPropertiesSet function---
----------IOC容器初始化完成----
----------准备关闭IOC容器----
---MyService2 DisposableBean destroy function---
----------IOC容器已关闭

方法三:在使用@Bean注解的时候指定其initMethoddestroyMethod

我们在src/main/java/service3里面新建MyService3,它实现了InitializingBean, DisposableBean接口

@Component
public class MyService3 {

    public void myInitMethod() {
        System.out.println("---MyService3 myInitMethod function---");
    }

    public void myDestoryMethod() {
        System.out.println("---MyService3 myDestoryMethod function---");
    }
}

然后在src/main/java/configuration里面新建NemoConfiguration3

@Configuration
@ComponentScan(basePackages = {"service3"})
public class NemoConfiguration3 {

    @Bean(initMethod = "myInitMethod", destroyMethod = "myDestoryMethod")
    public MyService3 myService3() {
        return new MyService3();
    }
}

然后在src/main/java里面新建一个NemoApplication3

public class NemoApplication3 {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(NemoConfiguration3.class);
        System.out.println("----------IOC容器初始化完成----");

        System.out.println("------准备关闭IOC容器----");
        // 关闭IOC容器
        ctx.close();
        System.out.println("------IOC容器已关闭");
    }
}

可以看到控制台上打印了以下内容,可以看到InitializingBean, DisposableBean的两个方法被调用了。

---MyService3 myInitMethod function---
----------IOC容器初始化完成----
----------准备关闭IOC容器----
---MyService3 myDestoryMethod function---
----------IOC容器已关闭

在使用@Bean注解的时候指定其initMethoddestroyMethod的时候要注意:

  • 指定的initMethod、destroyMethod不能有参数,否则会在IOC容器初始化的时候报错,报找不到该方法
  • 指定的initMethod、destroyMethod不需要返回值,不会报错,但是返回值对IOC容器来说也没啥用
  • 指定的initMethod、destroyMethod不可以抛出异常,否则会在IOC容器初始化的时候报错

上面演示了三种可以在bean初始化于销毁阶段做一些工作的方法,下面试一下当这三种方法同时存在的时候,调用的先后顺序是什么样的

我们在src/main/java/service4里面新建MyService4

@Component
public class MyService4 implements InitializingBean, DisposableBean {

    public void myInitMethod() {
        System.out.println("---MyService4 myInitMethod function---");
    }

    public void myDestoryMethod() {
        System.out.println("---MyService4 myDestoryMethod function---");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("---MyService4 InitializingBean afterPropertiesSet function---");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("---MyService4 DisposableBean destroy function---");
    }

    @PostConstruct
    public void initFunc() {
        System.out.println("---MyService4 @PostConstruct function");
    }

    @PreDestroy
    public void destoryFunc() {
        System.out.println("---MyService4 @PreDestroy function");
    }
}

然后在src/main/java/configuration里面新建NemoConfiguration4

@Configuration
@ComponentScan(basePackages = {"service4"})
public class NemoConfiguration4 {

    @Bean(initMethod = "myInitMethod", destroyMethod = "myDestoryMethod")
    public MyService4 myService4() {
        return new MyService4();
    }
}

然后在src/main/java里面新建一个NemoApplication4

public class NemoApplication4 {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(NemoConfiguration4.class);
        System.out.println("----------IOC容器初始化完成----");

        System.out.println("----------准备关闭IOC容器----");
        // 关闭IOC容器
        ctx.close();
        System.out.println("----------IOC容器已关闭");
    }
}

可以看到控制台上打印了以下内容,

---MyService4 @PostConstruct function
---MyService4 InitializingBean afterPropertiesSet function---
---MyService4 myInitMethod function---
----------IOC容器初始化完成----
----------准备关闭IOC容器----
---MyService4 @PreDestroy function
---MyService4 DisposableBean destroy function---
---MyService4 myDestoryMethod function---
----------IOC容器已关闭

从以上打印结果可以看出:

  • 初始化的时候,三种方法的先后顺序是:@PostConstruct -> InitializingBean接口的afterPropertiesSet -> @Bean指定的initMethod
  • 销毁的时候,三种方法的先后调用顺序是:@PreDestroy -> DisposableBean接口的destroy -> @Bean指定的destoryMethod

上面演示了三种方式,不过spring官方文档里面不建议使用InitializingBean、DisposableBean接口,因为其实没必要强绑定Spring的代码。

spring官方文档里面不建议使用InitializingBean、DisposableBean接口


三、bean的后置处理器

在第一部分,我们看到refresh方法里面有BeanPostProcessor相关的处理。下面来学习一下BeanPostProcessor。

BeanPostProcessor是一个接口,它有两个方法,从方法名上大致可以才出来,一个是在bean初始化之前会被调用,另一个是在bean初始化之后会被调用。

BeanPostProcessor的两个方法

首先看BeanPostProcessorpostProcessBeforeInitialization,它的入参是(Object bean, String beanName),那是不是意味着可以对IOC容器已经生成的bean做一些什么,比如说修改属性值。postProcessBeforeInitialization方法返回值类型是Object,那是不是意味着可以返回一个新的bean(甚至不是同一个类型的bean)来替代IOC容器为我们生成的bean?那如果返回的是null,IOC容器里面的这个bean也会变成null吗?

我们在src/main/java/entity里面新建几个类

// Document.java
public class Document {

    private String path;

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }
}

// Project.java
public class Project {

    private String title;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

// Task.java
public class Task {

    private String title;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

// User.java
public class User {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

然后在src/main/java/processor里面新建NemoProcessor1,实现BeanPostProcessor接口,然后在postProcessBeforeInitialization方法里面,针对beanName是project的bean,修改它的title属性。针对beanName是user的bean,返回null。针对beanName是document的bean,返回一个其他类型的对象。

public class NemoProcessor1 implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("--postProcessBeforeInitialization-beanName:" + beanName);
        if ("project".equals(beanName)) {

            Project originProject = (Project) bean;
            System.out.println("origin title=" + originProject.getTitle());
            originProject.setTitle("i am title of project");
            return originProject;
        }

        if ("user".equals(beanName)) {
            return null;
        }

        if ("document".equals(beanName)) {
            return new Task();
        }

        return bean;
    }
}

然后在src/main/java/configuration里面新建NemoConfiguration1:

@Configuration
public class NemoConfiguration1 {

    @Bean(name = "project")
    public Project project() {
        return new Project();
    }

    @Bean(name = "user")
    public User user() {
        return new User();
    }

    @Bean(name = "document")
    public Document document() {
        return new Document();
    }

    @Bean
    public NemoProcessor1 nemoProcessor1() {
        return new NemoProcessor1();
    }
}

然后在src/main/java里面新建一个NemoTestBeanProcessorApplication1

public class NemoTestBeanProcessorApplication1 {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(NemoConfiguration1.class);
        System.out.println("============IOC容器初始化好了===========");

        Project project = ctx.getBean(Project.class);
        System.out.println(project.getTitle());

        User user = ctx.getBean(User.class);
        System.out.println(user);

        Object document = ctx.getBean("document");
        System.out.println(document);
        System.out.println(document instanceof Document);
        System.out.println(document instanceof Task);

        System.out.println("============IOC容器开始销毁===========");
        ctx.close();
        System.out.println("============IOC容器销毁结束===========");
    }
}

可以看到控制台上打印了以下内容,

--postProcessBeforeInitialization-beanName:project
origin title=null
--postProcessBeforeInitialization-beanName:user
--postProcessBeforeInitialization-beanName:document
============IOC容器初始化好了===========
i am title of project
entity.User@7f77e91b
entity.Task@44a664f2
false
true
============IOC容器开始销毁===========
============IOC容器销毁结束===========

可以看到,当IOC容器初始化好了之后,project这个bean的title已经被修改了,user这个bean不是null,document这个bean不是null而且不再是Document类型而是Task类型。

那么我们可以得出结论:

  • postProcessBeforeInitialization里面如果返回null,那么IOC容器并不会真的把bean设置为null,而是还按原来的方式初始化bean
  • postProcessBeforeInitialization里面如果返回的是另外一个类型的Object,那么这个bean在IOC容器里面的beanName不会变,但是类型变了
  • postProcessBeforeInitialization里面可以修改bean的属性

下面来看下第二节所说的几种感知bean初始化的方法,与postProcessBeforeInitialization的先后顺序

Project为例,修改一下

public class Project implements InitializingBean, DisposableBean {

    private String title;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public void myInitMethod() {
        System.out.println("---Project myInitMethod function---");
    }

    public void myDestoryMethod() {
        System.out.println("---Project myDestoryMethod function---");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("---Project InitializingBean afterPropertiesSet function---");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("---Project DisposableBean destroy function---");
    }

    @PostConstruct
    public void initFunc() {
        System.out.println("---Project @PostConstruct function");
    }

    @PreDestroy
    public void destoryFunc() {
        System.out.println("---Project @PreDestroy function");
    }
}

还要修改一下配置类里面的project这个bean

    @Bean(name = "project", initMethod = "myInitMethod", destroyMethod = "myDestoryMethod")
    public Project project() {
        return new Project();
    }

打印结果为:

--postProcessBeforeInitialization-beanName:project
origin title=null
---Project @PostConstruct function
---Project InitializingBean afterPropertiesSet function---
---Project myInitMethod function---
--postProcessAfterInitialization-beanName:project
--postProcessBeforeInitialization-beanName:user
--postProcessAfterInitialization-beanName:user
--postProcessBeforeInitialization-beanName:document
--postProcessAfterInitialization-beanName:document
============IOC容器初始化好了===========
i am title of project
entity.User@4c40b76e
entity.Task@2ea6137
false
true
============IOC容器开始销毁===========
---Project @PreDestroy function
---Project DisposableBean destroy function---
---Project myDestoryMethod function---
============IOC容器销毁结束===========

Process finished with exit code 0

那么我们就从结果可以知道,bean初始化的时候的调用顺序是:BeanPostProcessorpostProcessBeforeInitialization -> @PostConstruct -> InitializingBean接口的afterPropertiesSet -> @Bean指定的initMethod -> BeanPostProcessorpostProcessAfterInitialization

翻一下org.springframework:spring-beans这个包,在里面找找还有没有其他的xxxBeanPostProcessor,发现了InstantiationAwareBeanPostProcessorDestructionAwareBeanPostProcessor,这两个接口都继承自BeanPostProcessor。可以从名字看出,这两个后置处理器可以在实例化销毁阶段让我们做一些处理。

看下InstantiationAwareBeanPostProcessor接口里面的方法,可以在实例化之前、实例化之后、设置属性值等阶段进行一些自定义操作。

接口里面的方法

看下DestructionAwareBeanPostProcessor接口里面的方法,可以在销毁之前进行一些自定义操作

接口里面的方法

按正常的处理逻辑的话,实例化和初始化的时候一般的调用顺序是这样的:InstantiationAwareBeanPostProcessorpostProcessBeforeInstantiation -> InstantiationAwareBeanPostProcessorpostProcessAfterInstantiation -> InstantiationAwareBeanPostProcessorpostProcessProperties -> BeanPostProcessorpostProcessBeforeInitialization -> @PostConstruct -> InitializingBean接口的afterPropertiesSet -> @Bean指定的initMethod -> BeanPostProcessorpostProcessAfterInitialization

我们来实验一下,在src/main/java/configuration里面新建一个NemoConfiguration2,里面的Project bean就是上文提到的Project,它实现了上文提到的三种感知初始化的方法。

@Configuration
public class NemoConfiguration2 {

    @Bean(name = "project", initMethod = "myInitMethod", destroyMethod = "myDestoryMethod")
    public Project project() {
        return new Project();
    }

    @Bean
    public NemoProcessor2 nemoProcessor2() {
        return new NemoProcessor2();
    }
}

下面来创建一个类,实现这两个接口,并且实现父接口BeanPostProcessor的方法

public class NemoProcessor2 implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {

    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("NemoProcessor2 postProcessBeforeInstantiation--" + "beanName:" + beanName + ";beanClass:" + beanClass);
        return null;
    }

    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        System.out.println("NemoProcessor2 postProcessAfterInstantiation--" + "beanName:" + beanName + ";bean:" + bean);

        // 如果返回false,则表示不调用后面的postProcessProperties方法
        return true;
    }

    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        System.out.println("NemoProcessor2 postProcessProperties--beanName:" + beanName + ";bean:" + bean + ";pvs:" + pvs);
        return null;
    }

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        System.out.println("NemoProcessor2 postProcessBeforeDestruction-object=" + bean + ";beanName=" + beanName);
    }

    @Override
    public boolean requiresDestruction(Object bean) {
        System.out.println("NemoProcessor2 requiresDestruction object=" + bean);

        // 如果返回的是false,则不会调用postProcessBeforeDestruction方法
        return true;
    }

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("--postProcessBeforeInitialization-beanName:" + beanName);
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("--postProcessAfterInitialization-beanName:" + beanName);
        return bean;
    }
}

然后我们在src/main/java下面新建一个NemoTestBeanProcessorApplication2,

public class NemoTestBeanProcessorApplication2 {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(NemoConfiguration2.class);
        System.out.println("============IOC容器初始化好了===========");

        System.out.println("============IOC容器开始销毁===========");
        ctx.close();
        System.out.println("============IOC容器销毁结束===========");
    }
}

控制台打印结果为:

NemoProcessor2 postProcessBeforeInstantiation--beanName:project;beanClass:class entity.Project
NemoProcessor2 postProcessAfterInstantiation--beanName:project;bean:entity.Project@15d9bc04
NemoProcessor2 postProcessProperties--beanName:project;bean:entity.Project@15d9bc04;pvs:PropertyValues: length=0
--postProcessBeforeInitialization-beanName:project
---Project @PostConstruct function
---Project InitializingBean afterPropertiesSet function---
---Project myInitMethod function---
--postProcessAfterInitialization-beanName:project
NemoProcessor2 requiresDestruction object=entity.Project@15d9bc04
============IOC容器初始化好了===========
============IOC容器开始销毁===========
NemoProcessor2 postProcessBeforeDestruction-object=entity.Project@15d9bc04;beanName=project
---Project @PreDestroy function
---Project DisposableBean destroy function---
---Project myDestoryMethod function---
============IOC容器销毁结束===========

和我们上面设想调用顺序一样,不过需要注意的是DestructionAwareBeanPostProcessorrequiresDestruction方法并不是等到bean销毁的时候才被调用,而是在初始化之后就被调用了。

我们下面对NemoProcessor2postProcessBeforeInstantiation做一些改动,在里面手动生成project的实例并返回

    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("NemoProcessor2 postProcessBeforeInstantiation--" + "beanName:" + beanName + ";beanClass:" + beanClass);
        if ("project".equals(beanName)) {
            Project project = new Project();
            project.setTitle("old title");
            return project;
        }

        return null;
    }

结果控制台的打印只有以下内容了 !!!!

NemoProcessor2 postProcessBeforeInstantiation--beanName:project;beanClass:class entity.Project
--postProcessAfterInitialization-beanName:project
============IOC容器初始化好了===========
============IOC容器开始销毁===========
============IOC容器销毁结束===========

可以想通的是,在postProcessBeforeInstantiation里面返回的不是null而是你自己new出来的一个实例,那么框架就认为,你不需要框架为你生成这个bean的实例,框架直接把你返回的实例存下来就好。不需要框架生成实例,也就不需要框架设置属性值,也不需要框架进行初始化。那么InstantiationAwareBeanPostProcessor接口里面的另外两个方法postProcessAfterInstantiationpostProcessProperties也就不会被触发,DestructionAwareBeanPostProcessor接口的两个方法也不会被触发,只会触发BeanPostProcessor里面的postProcessAfterInitialization方法。这一点在java的注释里面写得很清楚。

如果在postProcessBeforeInstantiation返回了一个非null

我们再来试一下其他,将NemoProcessor2的postProcessBeforeInstantiation变回原样,修改postProcessAfterInstantiation,让它返回false

    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("NemoProcessor2 postProcessBeforeInstantiation--" + "beanName:" + beanName + ";beanClass:" + beanClass);
        return null;
    }

    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        System.out.println("NemoProcessor2 postProcessAfterInstantiation--" + "beanName:" + beanName + ";bean:" + bean);

        // 如果返回false,则表示不调用后面的postProcessProperties方法
        if ("project".equals(beanName)) {
            return false;
        }
        return true;
    }

打印结果如下,可以看到,如果postProcessAfterInstantiation返回false,那么postProcessProperties方法就不会被调用了

NemoProcessor2 postProcessBeforeInstantiation--beanName:project;beanClass:class entity.Project
NemoProcessor2 postProcessAfterInstantiation--beanName:project;bean:entity.Project@15d9bc04
--postProcessBeforeInitialization-beanName:project
---Project @PostConstruct function
---Project InitializingBean afterPropertiesSet function---
---Project myInitMethod function---
--postProcessAfterInitialization-beanName:project
NemoProcessor2 requiresDestruction object=entity.Project@15d9bc04
============IOC容器初始化好了===========
============IOC容器开始销毁===========
NemoProcessor2 postProcessBeforeDestruction-object=entity.Project@15d9bc04;beanName=project
---Project @PreDestroy function
---Project DisposableBean destroy function---
---Project myDestoryMethod function---
============IOC容器销毁结束===========

我们再来看下postProcessProperties方法的作用,我们让NemoProcessor2的postProcessAfterInstantiation返回true,那么postProcessProperties方法就会被调用。在postProcessProperties方法里面设置project的title的值

    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        System.out.println("NemoProcessor2 postProcessProperties--beanName:" + beanName + ";bean:" + bean + ";pvs:" + pvs);
        if ("project".equals(beanName)) {
            MutablePropertyValues propertyValues = new MutablePropertyValues(pvs);
            propertyValues.addPropertyValue("title", "title set in postProcessProperties");
            return propertyValues;
        }
        return null;
    }

我们在resources目录下面新建一个属性文件project.properties,里面有一个值

project.title=TitleInPropertiesFile

假设我们给Project的title属性设置一个值,让框架从属性文件里面读取值。

public class Project implements InitializingBean, DisposableBean {

    @Value("${project.title}")
    private String title;

    ……
    ……

然后在NemoConfiguration2配置类上面标上注解@PropertySource("project.properties")

最后在控制台我们看到打印结果如下,可以看到在postProcessProperties被调用的时候,其实bean的属性还没有设置,如果在postProcessProperties返回的不是null,就会拦截框架对属性的注入。

NemoProcessor2 postProcessBeforeInstantiation--beanName:project;beanClass:class entity.Project
NemoProcessor2 postProcessAfterInstantiation--beanName:project;bean:Project{title='null'}
NemoProcessor2 postProcessProperties--beanName:project;bean:Project{title='null'};pvs:PropertyValues: length=0
--postProcessBeforeInitialization-beanName:project
---Project @PostConstruct function
---Project InitializingBean afterPropertiesSet function---
---Project myInitMethod function---
--postProcessAfterInitialization-beanName:project
NemoProcessor2 requiresDestruction object=Project{title='title set in postProcessProperties'}
============IOC容器初始化好了===========
title set in postProcessProperties
============IOC容器开始销毁===========
NemoProcessor2 postProcessBeforeDestruction-object=Project{title='title set in postProcessProperties'};beanName=project
---Project @PreDestroy function
---Project DisposableBean destroy function---
---Project myDestoryMethod function---
============IOC容器销毁结束===========

那么关于bean的创建过程,可以用下面这个图来表示:

bean life cycle-create

关于bean的销毁过程,可以用下图来表示

bean life cycle-destroy

Show Comments