赞
踩
Spring Bean的生命周期是一个老生常谈的问题了,网上一搜一大把,无非就是画一幅流程图(比如下面这幅图),然后用语言介绍创建bean后执行各Aware接口,然后BeanPostProcessor.....最终Bean创建成功了,就可以使用这个Bean了,然后在容器销毁的时候,又会执行一些操作。
其实对于上面的提到的流程图,注意上面的图只是Spring Bean的大概流程(省略了一部分),主要涉及到了5个接口,分别是XxxAware、BeanPostProcessor、InitiailizingBean、Destruction、DisposableBean接口,本文将会对这几个接口,以及init-method、destroy-method做相关的使用介绍,在明白怎么使用后,再把他们串起来,这样的话,对于Spring Bean的生命周期就差不多知道咋回事了,而不用死记硬背。
在说Aware、BeanPostProcessor、InitiailizingBean、Destruction、DisposableBean这些接口前,先简单回顾一下使用xml配置并获取一个Student类的bean过程,后面介绍各个接口的使用方式时时,也是按照这个形式;
平淡无奇的Student类:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
平淡无奇的applicationContext.xml配置文件,创建一个student bean,利用setter方式设置初始值:
1 2 3 4 5 6 7 8 9 10 11 |
|
创建一个Main类,用于测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
下面是运行程序的输出,可以看到和预期相符,创建一个Student的bean,id和name默认值为99、张三;
1 |
|
Aware接口有很多实现类,本文只介绍BeanNameAware、BeanFactoryAware、ApplicationContextAware,关系如下:
创建一个Student类,让该类实现BeanNameAware接口,并且重写setBeanName方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
配置文件和测试程序都不改变,运行测试程序,输出内容如下:
1 2 3 |
|
可以看到,实现BeanNameAware接口后,重写setBeanName的方法中,获取到的student bean,是已经初始化的bean(属性都已经有值了),并且setBeanName方法中可以对当前的bean进行各种操作,包括修改bean的某些属性,最后获取到的bean是已经修改后的bean。
这里只是简单介绍了一下BeanNameAware接口的用法,使用BeanNameAware接口,可以对当前Bean进行操作。
创建Student类,实现BeanFactoryAware接口,并且重写setBeanFactory方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
运行输出如下:
1 2 3 |
|
通过上面的代码输出结果可以看出,实现BeanFactoryAware接口后,可以在setBeanFactory方法中操作BeanFactory的所有bean,操作的范围要比BeanNameAware要大。
ApplicationContext,有多种称呼,比如“应用容器”、“环境”、“上线文”...
创建Student类,实现ApplicationContextAware接口,并且重写setApplicationContext接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
需要修改一下测试程序,测试程序中加载配置时使用的XmlBeanFactory,而XmlBeanFactory不会回调ApplicationContextAware接口的setApplicationContext方法,下面使用ClassPathXmlApplicationContext类来加载配置:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
运行测试程序:
1 2 3 4 |
|
实现ApplicationContextAware接口后,在setApplicationContext方法中,入参是当前的applicationContext,也就是说,可以在该方法中对Spring容器进行设置,操作的范围又要比BeanFactoryAware的setBeanFactory要广得多。
既然有这几个Aware接口,如果一个类同时实现了这3个接口,那么执行顺序是怎样的呢?下面就来测试一下。
创建Student类,分别实现BeanNameAware、BeanFactoryAware、ApplicationContextAware接口,并重写其接口的方法:
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 |
|
仍旧使用ClassPathXmlApplicationContext类来加载配置,运行输出结果如下:
1 2 3 4 |
|
上面演示了Spring中几个Aware接口的用法和特点,下面总结一下:
1.实现BeanNameAware接口后,重写setBeanName方法,可以对单个Bean进行扩展修改;
2.实现BeanFactoryAware接口后,重写setBeanFactory方法,可以对bean工厂中的所有Bean进行扩展修改;
3.实现ApplicationContextAware接口后,重写setApplicationContext方法后,可以对整个容器进行扩展修改;
4.这几个接口的执行顺序分别是BeanNameAware->BeanFactoryAware->ApplicationContextAware;
BeanPostProcessor和前面的Aware接口有些区别,通过下面的例子就能看出区别在哪里!
下面举个例子,创建MyBeanPostProcessor类,实现BeanPostProcessor接口,注意,这里没有在Student类上实现BeanPostProcessor接口。
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 |
|
创建两个类,分别是Student和User类,其中Use类没有实现Aware接口,Student类实现了前面提到的3个Aware接口
1 2 3 4 5 |
|
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 |
|
xml配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
测试:
1 2 3 4 5 6 7 8 |
|
从上面的运行结果可以得出以下结论:
1.因为只有Student实现了Aware接口,所以创建student bean的时候会调用对应的Aware接口方法,而User类没有实现Aware接口,所以并没有调用Aware接口方法;
2.Student和User类都没有继承BeanPostProcessor接口,但是在创建student和user bean的时候,都掉用了MyBeanPostProcessor类中的前置和后置处理(继承自BeanPostProcessor接口);
3.BeanPostProcessor接口的前置和后置处理,是在Aware接口之后调用;
4.很重要的一点,需要将BeanPostProcessor接口实现类声明为bean,使用<bean>配置或者使用@Component注解,不然BeanPostProcessor不起作用。
创建Student类,实现InitializingBean接口,然后重写afterPropertiesSet方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
修改xml配置文件,创建student bean,测试:
1 2 |
|
创建Student类,增加一个额外的方法display()
1 2 3 4 5 6 7 8 9 10 11 |
|
修改配置文件,在<bean>标签中增加init-method属性,值为display,也就是Student的display方法名:
1 2 3 4 5 6 7 8 9 10 11 |
|
运行测试:
1 2 |
|
上面,输出了display中的内容,这是在设置bean的时候调用的。
DestructionAwareBeanPostProcessor接口,从名称上可以看出来是DestructionAware + BeanPostProcessor的组合,其实也的确是这样,但是需要注意的就是,spring并没有提供DestructionAware接口!!
下面是DestructionAwareBeanPostProcessor接口的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
DestructionAwareBeanPostProceesor继承自BeanPostProcessor接口,所以也可以重写前值和后置处理。
下面介绍使用示例,创建MyDestructionAwareBeanPostProceesor,继承DestructionAwareBeanPostProceesor接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
修改配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
测试程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
运行测试程序,输出如下:
1 2 3 |
|
可以看到,在手动调用destroyBean方法来销毁student bean的时候,调用了MyDestructionAwareBeanPostProcessor中定义的方法。
需要注意的是,虽然这里使用destroyBean来销毁了student bean,如果又通过getBean来获取student bean,则会重新创建student bean。
前面介绍了DestructionAwareBeanPostProcessor接口,可以对所有的bean设置销毁(destruction)后的处理操作。
而这里介绍的DisposableBean接口,就是对单独的Bean进行destrction后的处理,也就是说不是应用到所有的bean上。
简单介绍一下用法,创建Student类和User类,User类正常(不实现任何接口),Student类实现DisposableBean接口,然后重写destroy方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
创建配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
测试程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
运行输出:
1 2 3 |
|
可以看到,虽然测试代码中destroy了student和user两个bean,但是只有student bean在销毁时触发了DisposableBean的destory方法。
和init-method相对应的就是destory-method方法了,创建Student类,增加clean方法(自定义):
1 2 3 4 5 6 7 8 9 10 11 |
|
修改配置文件,<bean>标签中使用destroy-method属性,值为clean方法
1 2 3 4 |
|
测试程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
输出:
1 2 |
|
上面对每一种接口都做了介绍,这里就将所有接口都做一下整合,尝试在一个测试程序中测试所有接口,这个过程中就会对Bean的生命周期有清晰的认识:
创建Student类,实现Aware、InitializingBean、DisposableBean接口,并且增加display、clean方法,作为init-method和destory-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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
|
创建MyBeanPostProcessor接口实现类,并重写前置和后置处理方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
创建MyDestructionAwareBeanPostProcessor类,并重写其中的方法(不重写BeanPostProcessor的前后置处理方法):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
看了上面这个输出结果,再结合下面这个图,基本就能掌握Bean的大致生命周期了。
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。