Spring Bean 在应用上下文中的生命周期

Spring 中 Bean 的生命周期由多个特定的生命阶段组成,每个阶段都能对 Bean 进行控制。本文中的生命周期为 ApplicationContext 中 Bean 的生命周期

两个生命周期方法

先来看看两个生命周期的方法。

Spring 初始化 bean 或销毁 bean 时,有时需要作一些处理工作,因此 spring 可以在创建和拆卸 bean 的时候调用 bean 的两个生命周期方法。

  • @PostConstruct:当 bean 被载入到容器时调用方法的注解;
  • @PreDestroy:当 bean从容器中删除的时候调用方法的注解

如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component("bean")
public class Bean {
@PostConstruct
public void setup() {
System.out.println("initialize bean...");
}

public void say() {
System.out.println("say...");
}

@PreDestroy
public void teardown() {
System.out.println("destroy bean...");
}
}

XML 方式为:

1
2
3
<bean id=“bean” class=“...Bean” 
init-method=“setup”
destory-method=“teardown”/>

从容器中删除时的方法只对 scope 为 singleton 时有效。

完整生命周期

先列出生命周期过程,示例代码放在后面:

  1. instantiate bean 对象实例化;
  2. populate properties 封装属性;
  3. 如果 Bean 实现 BeanNameAware 则执行 setBeanName
  4. 如果 Bean 实现 BeanFactoryAware 或者 ApplicationContextAware 则设置工厂 setBeanFactory 或者上下文对象 setApplicationContext
  5. 如果存在类实现 BeanPostProcessor(后处理 Bean),则执行 postProcessBeforeInitialization
  6. 如果 Bean 实现 InitializingBean 则执行 afterPropertiesSet
  7. 调用 <bean init-method="setUp"> 指定初始化方法 setUp
  8. 如果存在类实现 BeanPostProcessor(处理Bean),则执行 postProcessAfterInitialization
  9. 执行业务处理;
  10. 如果 Bean 实现 DisposableBean 则执行 destroy
  11. 调用 <bean destroy-method="tearDown"> 指定销毁方法 tearDown

定义一个类 Man,包含了能显示周期过程的一些方法,Bean 管理用 XML 方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.imtt.ioc.demo;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class Man implements BeanNameAware, ApplicationContextAware, InitializingBean, DisposableBean {
private String name;

public String getName() {
return name;
}

public Man() {
System.out.println("1. Man 实例化");
}

public void setName(String name) {
System.out.println("2. 设置属性");
this.name = name;
}

@Override
public void setBeanName(String s) {
System.out.println("3. 设置Bean名称" + s);
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("4. 了解工厂信息");
}

@Override
public void afterPropertiesSet() {
System.out.println("6. 属性设置后");
}

public void setUp() {
System.out.println("7. Man 初始化");
}

public void run() {
System.out.println("9. 执行自身业务方法");
}

public void tearDown() {
System.out.println("11. Man 销毁");
}

@Override
public void destroy() {
System.out.println("10. Spring的销毁");
}
}
1
2
3
4
<bean id="man" class="com.imtt.ioc.demo.Man"
init-method="setUp" destroy-method="tearDown">
<property name="name" value="Twu"/>
</bean>

第三步和第四步是让当前类了解其在 Spring 中一些信息:

  • 第三步实现 BeanNameAware 接口,实现 setBeanName 方法设置 Bean 名称;
  • 第四步实现 ApplicationContextAware 接口,实现 setApplicationContext 方法了解工厂信息;

第五步和第八步分别在初始化 Bean 实例之前和之后执行:

  • 实现 BeanPostProcessor 接口,包含了 postProcessBeforeInitialization 方法和 postProcessAfterInitialization 方法。

定义一个类 MyBeanPostProcessor ,实现了 BeanPostProcessor 接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.imtt.ioc.demo3;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("5. 初始化前");
return bean;
}

@Override
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
System.out.println("8. 初始化后");
return bean;
}
}
1
<bean class="com.imtt.ioc.demo3.MyBeanPostProcessor"/>

第六步实现接口 InitializingBean 的话,要实现方法 afterPropertiesSet,Bean 属性设置后执行;

第七步执行自己配置的 setUp方法;

第九步执行类自己本身的方法,如方法 run

第十步实现接口 DisposableBean 的后,要实现方法 destroy,执行 Spring 的销毁;

第十一步执行自己配置的销毁方法 tearDown

使用单元测试运行一下:

1
2
3
4
//other codes here.
Man man = (Man) applicationContext.getBean("man");
System.out.println(man);
((ClassPathXmlApplicationContext) applicationContext).close();

输出的结果如下:

1
2
3
4
5
6
7
8
9
10
11
1. Man 实例化
2. 设置属性
3. 设置Bean名称man
4. 了解工厂信息
5. 初始化前
6. 属性设置后
7. Man 初始化
8. 初始化后
9. 执行自身业务方法
10. Spring的销毁
11. Man 销毁

后处理 Bean

生命周期中有一个重要的类 BeanPostProcessor,是整个类生成过程中要执行的一部分,对类产生代理和方法进行增强,这其实涉及了 AOP 思想。

定义一个接口 UserDao 及其实现 UserDaoImpl,其中包含了两个方法 saveupgrade

1
2
3
4
5
6
7
package com.imtt.ioc.demo1;

public interface UserDao {
public void save();

public void upgrade();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.imtt.ioc.demo1;

public class UserDaoImpl implements UserDao{
@Override
public void save() {
System.out.println("保存");
}

@Override
public void upgrade() {
System.out.println("修改");
}
}

Bean 注解:

1
<bean id="userDao" class="com.imtt.ioc.demo1.UserDaoImpl"/>

单元测试:

1
2
3
4
//other codes here.
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.save();
userDao.upgrade();

接下来专门针对 save 方法进行增强,修改 postProcessAfterInitialization 方法中的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//java.lang.reflect.InvocationHandler;
//java.lang.reflect.Method;
//java.lang.reflect.Proxy;

@Override
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
if (beanName.equals("userDao")) {
return (Object) Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("save".equals(method.getName())) {
System.out.println("--->权限校验");
}
return method.invoke(bean, args);
}
});
} else {
return bean;
}
}

意思是如果 Bean 的名称是 “userDao”,且方法名是 “save“ 的话,先打印一条语句 “—>权限校验”。

单元测试后打印:

1
2
3
--->权限校验
保存
修改
坚持原创技术分享,您的支持将鼓励我继续创作!