Spring核心技术之定制bean属性
Spring提供了几个标志接口,用来改变容器中bean的行为。包括InitializingBean和DisposableBean.实现这两个接口的bean在初始化和析构时,容器都会调用前者的afterPropertiesSet()方法,以及后者的destroy()方法。
Spring在内部使用BeanPostProcessor实现来处理它能找到的任何标志接口并调用相应的方法。如果需要自定义特性或者生命周期行为,可以实现自己的BeanPostProcessor。关于这个请参照之前的容器扩展点。
1)初始化回调:
package springapp.test.beanCustomizing; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; /** * @author zhangxuegang * @version 1.0 * @created 2012-10-19 上午10:30:23 */ public class ImplCustom implements InitializingBean,DisposableBean { public ImplCustom(){ System.out.println("this is "+ImplCustom.class+"`s constractor method"); } @Override //InitializingBean public void afterPropertiesSet() throws Exception { System.out.println("in "+ ImplCustom.class+" afterPropertiesSet() method"); } @Override //DisposableBean public void destroy() throws Exception { System.out.println("in "+ ImplCustom.class+" destroy() method"); }
Xml配置
<bean id="implCustom" class="springapp.test.beanCustomizing.ImplCustom"></bean>
测试代码:
package springapp.test.beanCustomizing; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * * * @author zhangxuegang * @version 1.0 * @created 2012-10-19 上午10:33:16 */ public class TestCustom { public static void main(String[] args) throws Exception{ ApplicationContext ctx = new ClassPathXmlApplicationContext( "file:F:/springProject/springapp/war/WEB-INF/applicationTest.xml"); ImplCustom impl = (ImplCustom) ctx.getBean("implCustom",ImplCustom.class); ((AbstractApplicationContext) ctx).registerShutdownHook(); } }
输出:
this is class springapp.test.beanCustomizing.ImplCustom`s constractor method in class springapp.test.beanCustomizing.ImplCustom afterPropertiesSet() method in class springapp.test.beanCustomizing.ImplCustom destroy() method
不过我们应该尽量避免使用这种方法,在上面的例子中我们可以看出,这种方法是与SPRING耦合在一起的,所以我们尽量使用普通的初始化方法即在XML配置文件中通过指定init-method属性来完成。
修改后的代码:
package springapp.test.beanCustomizing; /** * @author zhangxuegang * @version 1.0 * @created 2012-10-19 上午10:30:23 */ public class ImplCustom1 { public ImplCustom1(){ System.out.println("this is "+ImplCustom1.class+"`s constractor method"); } public void init(){ System.out.println("脱离spring,通过配置文件进行bean的定制"); } }
xml:
<bean id="implCustom1" class="springapp.test.beanCustomizing.ImplCustom1" init-method="init"></bean>
测试代码:
package springapp.test.beanCustomizing; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * * * @author zhangxuegang * @version 1.0 * @created 2012-10-19 上午10:33:16 */ public class TestCustom { public static void main(String[] args) throws Exception{ ApplicationContext ctx = new ClassPathXmlApplicationContext( "file:F:/springProject/springapp/war/WEB-INF/applicationTest.xml"); ImplCustom1 impl1 = (ImplCustom1) ctx.getBean("implCustom1",ImplCustom1.class); ((AbstractApplicationContext) ctx).registerShutdownHook(); } }
输出:
this is class springapp.test.beanCustomizing.ImplCustom`s constractor method in class springapp.test.beanCustomizing.ImplCustom afterPropertiesSet() method this is class springapp.test.beanCustomizing.ImplCustom1`s constractor method
脱离spring,通过配置文件进行bean的定制
in class springapp.test.beanCustomizing.ImplCustom destroy() method
2)析构回调
实现org.springframework.beans.factory.DisposableBean接口的bean允许在容器销毁该bean的时候获得一次回调。DisposableBean接口也只规定了一个方法void destroy() throws Exception;通常要避免使用DisposableBean标志接口,原因同上。我们可以在bean定义中指定一个普通的析构方法,即在XML配置文件中通过指定destroy-method属性来完成。
因为上边的例子中已经使用了析构回调所以不在进行讲解
3)缺省的初始化和析构方法
如果我们都是通过设置bean的init-method或者destroy-method属性,由于初始化方法和析构方法的名字是可以任意指定的,所以这种回调方法的名字将各式各样,按照经验来说,最好在一个项目范围内标准化。
那么如果将spring容器配置成在每个bean上查找事先指定好的初始化和析构回调方法名称。这样可以简化bean定义。例如,如果按照这样<beans default-init-method="init" >
<bean id="blogServic" class="com.foo.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>来配置的话,Spring Ioc容器会在bean被创建的时候调用该init方法。当然在DefaultBlogService类中要有public void init(){}方法。这样就不需要在每个bean中都配置init-mehtod属性了!同样,配置析构方法回调是在顶层<beans/>元素上使用default-destroy-method属性。
最后补充一点,如果实际的回调方法与默认的命名约定不同时,那么可以通过在bean元素上使用init-method和destroy-method属性来指定方法名来覆盖缺省设置。
在Spring2.5中有三种方式可以控制bean的生命周期行为: InitializingBean
和 DisposableBean
回调接口;自定义init()
和 destroy()
方法; @PostConstruct
和@PreDestroy
annotations.
当组合不同的生命周期机制时 - 例如,类层次中使用了不同的生命周期机制 - 开发者必须注意这些机制的应用顺序,下面是初始化方法中的顺序:
- @PostConstruct元注释
- InitializingBean的afterPropertiesSet()定义
- 自定义init()方法配置
析构方法的调用和初始化方法的顺序是一致的
注意
如果bean存在多种的生命周期机制配置并且每种机制都配置为不同的方法名, 那所有配置的方法将会按照上面的顺利执行。然而如果配置了相同的方法名 - 例如, init()初始化方法 - 采用多种机制配置后,只会执行一次。
3.5.1.5. 在非web应用中优雅地关闭Spring IoC容器
注意
在基于web的ApplicationContext
实现中已有相应的代码来处理关闭web应用时如何恰当地关闭Spring IoC容器。
如果你正在一个非web应用的环境下使用Spring的IoC容器,例如在桌面富客户端环境下,你想让容器优雅的关闭,并调用singleton bean上的相应析构回调方法,你需要在JVM里注册一个“关闭钩子”(shutdown hook)。这一点非常容易做到,并且将会确保你的Spring IoC容器被恰当关闭,以及所有由单例持有的资源都会被释放(当然,为你的单例配置销毁回调,并正确实现销毁回调方法,依然是你的工作)。
为了注册“关闭钩子”,你只需要简单地调用在AbstractApplicationContext
实现中的registerShutdownHook()
方法即可。也就是:
其实上边的测试方法中我们用的就是此类方法:
package springapp.test.beanCustomizing; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * * * @author zhangxuegang * @version 1.0 * @created 2012-10-19 上午10:33:16 */ public class TestCustom { public static void main(String[] args) throws Exception{ ApplicationContext ctx = new ClassPathXmlApplicationContext( "file:F:/springProject/springapp/war/WEB-INF/applicationTest.xml"); ImplCustom1 impl1 = (ImplCustom1) ctx.getBean("implCustom1",ImplCustom1.class); ((AbstractApplicationContext) ctx).registerShutdownHook(); } }
北京市海淀区
邮箱:rafx_z@hotmail.com