spring容器初始化Bean操作
在某些情况下,Spring容器在初始化Bean的时候,希望在初始化bean前和销毁bean前进行一些资源的加载和释放的操作。可以通过一下三种方式完成。
- Bean的方法加上@PostConstruct和@PreDestroy注解
- 在xml中定义init-method和destory-method方法
- Bean实现InitializingBean和DisposableBean接口
@PostConstruct和@PreDestroy注解
JavaBean代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@Component public class PersonService { private String message; public String getMessage() { return message; } public void setMessage(String message) { this .message = message; } @PostConstruct public void init() { System.out.println( "PersonService.class init method ..." ); } @PreDestroy public void cleanUp() { System.out.println( "PersonService.class cleanUp method ..." ); } } |
spring配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<? xml version = "1.0" encoding = "UTF-8" ?> < beans xmlns = "http://www.springframework.org/schema/beans" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:context = "http://www.springframework.org/schema/context" xmlns:aop = "http://www.springframework.org/schema/aop" xmlns:mvc = "http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd"> <!-- spring扫描的路径 --> < context:component-scan base-package = "spring.zhujie" /> </ beans > |
测试代码和结果
测试代码
1
2
3
4
|
public static void main(String[] args) { AbstractApplicationContext context = new ClassPathXmlApplicationContext( "spring-zhujie.xml" ); context.registerShutdownHook(); } |
运行结果
PersonService.class init method ...
PersonService.class cleanUp method ...
在XML中定义init-method和destory-method方法
JavaBean代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class PersonService { private String message; public String getMessage() { return message; } public void setMessage(String message) { this .message = message; } public void init() { System.out.println( "PersonService.class init method ..." ); } public void cleanUp() { System.out.println( "PersonService.class cleanUp method ..." ); } } |
spring配置文件
1
|
< bean class = "spring.zhujie.PersonService" init-method = "init" destroy-method = "cleanUp" /> |
测试代码和结果
测试代码
1
2
3
4
|
public static void main(String[] args) { AbstractApplicationContext context = new ClassPathXmlApplicationContext( "spring-xml.xml" ); context.registerShutdownHook(); } |
运行结果
PersonService.class init method ...
六月 23, 2017 9:42:06 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@7a94c5e7: startup date [Fri Jun 23 21:42:06 CST 2017]; root of context hierarchy
PersonService.class cleanUp method ...
Bean实现InitializingBean和DisposableBean接口
JavaBean代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public class PersonService implements InitializingBean, DisposableBean { private String message; public String getMessage() { return message; } public void setMessage(String message) { this .message = message; } @Override public void afterPropertiesSet() throws Exception { System.out.println( "PersonService.class init method ..." ); } @Override public void destroy() throws Exception { System.out.println( "PersonService.class cleanUp method ..." ); } } |
spring配置文件
1
|
< bean id = "personService" class = "spring.zhujie.PersonService" /> |
测试代码和结果
测试代码
1
2
3
4
|
public static void main(String[] args) { AbstractApplicationContext context = new ClassPathXmlApplicationContext( "spring-interface.xml" ); context.registerShutdownHook(); } |
运行结果
PersonService.class init method ...
PersonService.class cleanUp method ...
Spring bean 初始化顺序
InitializingBean, init-method 和 PostConstruct
1、概述
从接口的名字上不难发现,InitializingBean 的作用就是在 bean 初始化后执行定制化的操作。
Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,常用的设定方式有以下三种:
通过实现 InitializingBean/DisposableBean 接口来定制初始化之后/销毁之前的操作方法;
通过 <bean> 元素的 init-method/destroy-method 属性指定初始化之后 /销毁之前调用的操作方法;
在指定方法上加上@PostConstruct 或@PreDestroy注解来制定该方法是在初始化之后还是销毁之前调用。
2、InitializingBean vs init-method
接口定义如下:
1
2
3
|
public interface InitializingBean { void afterPropertiesSet() throws Exception; } |
接口只有一个方法afterPropertiesSet,
此方法的调用入口是负责加载 spring bean 的AbstractAutowireCapableBeanFactory,源码如下:
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
|
protected void invokeInitMethods(String beanName, Object bean, RootBeanDefinition mbd) throws Throwable { boolean isInitializingBean = bean instanceof InitializingBean; if ((isInitializingBean) && (((mbd == null ) || (!(mbd .isExternallyManagedInitMethod( "afterPropertiesSet" )))))) { if ( this .logger.isDebugEnabled()) { this .logger .debug( "Invoking afterPropertiesSet() on bean with name '" + beanName + "'" ); } //先调用afterPropertiesSet()进行初始化 if (System.getSecurityManager() != null ) { try { AccessController.doPrivileged( new PrivilegedExceptionAction(bean) { public Object run() throws Exception { ((InitializingBean) this .val$bean) .afterPropertiesSet(); return null ; } }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { ((InitializingBean) bean).afterPropertiesSet(); } } //然后调用InitMethod()进行初始化 if (mbd != null ) { String initMethodName = mbd.getInitMethodName(); if ((initMethodName == null ) || ((isInitializingBean) && ( "afterPropertiesSet" .equals(initMethodName))) || (mbd.isExternallyManagedInitMethod(initMethodName))) return ; invokeCustomInitMethod(beanName, bean, mbd); } } |
从这段源码可以得出以下结论:
spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中通过init-method指定,两种方式可以同时使用
实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对spring的依赖
先调用afterPropertiesSet,再执行 init-method 方法,如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法
3、@PostConstruct
通过 debug 和调用栈找到类InitDestroyAnnotationBeanPostProcessor, 其中的核心方法,即 @PostConstruct 方法调用的入口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass()); try { metadata.invokeInitMethods(bean, beanName); } catch (InvocationTargetException ex) { throw new BeanCreationException(beanName, "Invocation of init method failed" , ex.getTargetException()); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Failed to invoke init method" , ex); } return bean; } |
从命名上,我们就可以得到某些信息——这是一个BeanPostProcessor。BeanPostProcessor的postProcessBeforeInitialization是在Bean生命周期中afterPropertiesSet和init-method之前被调用的。另外通过跟踪,@PostConstruct方法的调用方式也是通过反射机制。
4、小结一下吧
spring bean的初始化执行顺序:构造方法 --> @PostConstruct注解的方法 --> afterPropertiesSet方法 --> init-method指定的方法。具体可以参考例子
afterPropertiesSet通过接口实现方式调用(效率上高一点),@PostConstruct和init-method都是通过反射机制调用
同理,bean销毁过程的顺序为:@PreDestroy > DisposableBean > destroy-method
不再展开,看源码就好
测试代码如下:
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
|
@Slf4j public class InitSequenceBean implements InitializingBean { public InitSequenceBean() { log.info( "InitSequenceBean: construct" ); } @Override public void afterPropertiesSet() throws Exception { log.info( "InitSequenceBean: afterPropertiesSet" ); } @PostConstruct public void postConstruct() { log.info( "InitSequenceBean: postConstruct" ); } public void initMethod() { log.info( "InitSequenceBean: initMethod" ); } } @Configuration public class SystemConfig { @Bean (initMethod = "initMethod" , name = "initSequenceBean" ) public InitSequenceBean initSequenceBean() { return new InitSequenceBean(); } } @Slf4j public class InitSequenceBeanTest extends ApplicationTests { @Autowired private InitSequenceBean initSequenceBean; @Test public void initSequenceBeanTest() { log.info( "Finish: {}" , initSequenceBean.toString()); } } |
以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/zbw18297786698/article/details/7365646