自定义Bean的性质
-
生命周期回调
1.1 初始化回调
如果我们的Bean实现
The org.springframework.beans.factory.InitializingBean
接口,当容器将所有必要的属性注入该bean之后,我们实现的afterPropertiesSet()
方法将会被回调。<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean { @Override public void afterPropertiesSet() { // do some initialization work } }
但是这样做就将业务代码与Spring框架耦合了。
在基于XML的配置中,为
<bean>
元素的init-method
属性指定一个初始化方法,可以起到相同的效果。<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean { public void init() { // do some initialization work } }
在基于注解的配置中,使用
@PostConstruct
对初始化方法进行注解:@Bean public class ExampleBean { @PostConstruct public void init() { // do some initialization work } }
1.2 销毁回调
如我们的bean实现了
org.springframework.beans.factory.DisposableBean
接口,当管理该bean的容器被销毁后,我们实现的destroy()
方法将会被回调。<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean { @Override public void destroy() { // do some destruction work (like releasing pooled connections) } }
但是这与Spring框架耦合了。
在基于XML的配置中,为
<bean>
元素的destroy-method
属性指定一个销毁方法,可以起到相同的效果。<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean { public void cleanup() { // do some destruction work (like releasing pooled connections) } }
在基于注解的配置中,使用
@PreDestroy
:@Bean public class ExampleBean { @PreDestroy public void init() { // do some initialization work } }
1.3 默认初始化和销毁方法
如果在整个项目中,都使用相同的名称为bean定义初始化和销毁方法,那么我们就可以配置默认的初始化和销毁回调方法名。
<beans defualt-init-method="init" defualt-destroy-method="cleanup"> <bean id="blogService" class="com.something.DefaultBlogService"> <property name="blogDao" ref="blogDao" /> </bean> </beans>
public class DefaultBlogService implements BlogService { private BlogDao blogDao; public void setBlogDao(BlogDao blogDao) { this.blogDao = blogDao; } // this is (unsurprisingly) the initialization callback method public void init() { if (this.blogDao == null) { throw new IllegalStateException("The [blogDao] property must be set."); } } }
1.4 联合生命周期机制(多种配置方式混用)
我们可以同时使用多种方式来定义bean的生命周期回调,如果每种方式对应不同的方法,那么这些方法将按下面的顺序回调。
初始化方法:
- 被
@PostConstruct
注解的方法 - 实现
InitializingBean
接口的afterPropertiesSet
方法 - XML中
<bean>
元素的init-method
指定的方法
销毁方法: - 被
@PreDestroy
注解的方法 - 实现
DisposableBean
接口的destroy
方法 - XML中
<bean>
元素的destroy-method
指定的方法
1.5 ·Startup和Shutdown回调
org.springframework.context.Lifycycle
接口为任意有自己生命周期的对象定义了重要的方法:public interface Lifecycle{ void start(); void stop(); boolean isRunning(); }
任何被容器管理的对象都可以继承这个接口,当容器接收到
start
和stop
信号后,会将这个调用传递给实现这个接口的对象。org.springframework.context.Lifecycle
接口只是对start和stop通知的简单定义,它并不会在容器刷新是自动启动,我们应该实现org.springframework.context.SmartLifecycle
public interface Phased { int getPhase(); } public interface SmartLifecycle extends Lifecycle, Phased { boolean isAutoStartup(); void stop(Runnable callback); }
当我们实现
SmartLifecycle
接口的isAutoStartup
方法时,如果返回true
,那么在容器刷新时,就会自动调用start
方法(Lifecycle
的实现);如果返回false
,那么就只有显示调用这个方法,或者调用容器的start
方法时自动回调bean的start
方法。Phased
接口的getPhase
方法的返回值将决定哪个bean的start
方法先被调用。phase值越小,越先调用start
,越后调用stop(Runnable)
;普通bean(没有继承Lifecycle
接口的bean)phase默认为0。下面是一个简单的例子:
public class MyTask implements SmartLifecycle { public static final Logger log = Logger.getLogger("com.heisenberg.spring"); Thread t; boolean isRunning = false; public void start() { MyRunnable runnable = new MyRunnable(); t = new Thread(runnable); t.start(); if (!t.isInterrupted()){ log.info(Utils.LOG_TAG + "Task is Running"+Utils.LOG_TAG); isRunning = true; } } public void stop() { t.interrupt(); } public boolean isRunning() { return isRunning; } public boolean isAutoStartup() { return false; } public void stop(Runnable callback) { this.stop(); if (t.isInterrupted()){ log.info(Utils.LOG_TAG + "Task stopped"+Utils.LOG_TAG); } callback.run(); } public int getPhase() { return -1; } } class MyRunnable implements Runnable{ public static final Logger log = Logger.getLogger("com.heisenberg.spring"); public void run() { while (!Thread.currentThread().isInterrupted()){ log.info(Utils.LOG_TAG + "Tick" + Utils.LOG_TAG); } } }
1.6 在非Web应用中优雅的关闭容器
public static void main(String[] args){ //使用AbstractApplicationContext或者ConfigurableApplicationContext都可以 //AbstractApplicationContext context = new ClassPathXmlApplicationContext("service.xml"); ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("service.xml"); //在JVM中注册一个关闭钩子 context.registerShutdownHook(); //下面是任务 //当任务完成后退出应用,在退出之前退出钩子会被回调,它将关闭容器 System.exit(0) }
- 被
-
ApplicationContextAware
和BeanNameAware
2.1
ApplicationContextAware
当一个
ApplicationContext
容器创建了一个实现了ApplicationContextAware
接口的类实例时,这个实例将拥有它所在容器的引用。但是并不推荐实现
ApplicationContextAware
接口,这会使代码跟Spring耦合,这也违背了控制反转的风格。我们也可以通过自动装配来获取容器的引用
2.2
BeanNameAware
当一个类实现了
BeanNameAware
接口,容器创建这个类的实例之后,该bean将会得到一个名称的引用。这个名称是其它bean对这个bean的引用的变量名称。public interface BeanNameAware { //name 便是其它bean对该bean的引用变量名 void setBeanName(String name) throws BeansException; }
-
其它的
Aware
接口其它的Aware接口