如何定制Spring Bean
Spring提供了一系列用于自定义Bean特性的接口,主要有以下3类:
生命周期回掉
为了能够与Spring管理的bean的生命周期交互,可以实现InitializingBean和DisposableBean接口,Spring容器会在初始化和销毁bean的时期分别回掉afterPropertiesSet()和destroy()方法
tips:Spring官方还是推荐使用JSR-250规范的
@PostConstruct和@PreDestroy注解进行生命周期回掉声明,因为这样不会耦合Spring的代码,当然如果既不想耦合Spring的代码又想进行生命周期回掉,可以给beanDefinition指定init-method和destroy-method属性
在Spring内部,Spring使用BeanPostProcessor来完成生命周期的各种回掉函数,如果想要自定义或者实现Spring没有提供的生命周期行为,可以考虑实现BeanPostProcessor来完成目标
除了生命周期回掉函数之外,Spring管理的对象也可能实现了Lifecycle接口,所以这些对象能够参与应用的启动与停止的过程。
初始化回掉
org.springframework.beans.factory.InitializingBean 接口能够在容器为Bean设置了所有必要的属性后执行初始化工作。
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}但是,秉承不耦合Spring的宗旨,建议使用 @PostConstruct注解或指定生命周期回掉函数。比如通过@Bean注解:
public class BeanOne {
public void init() {
// initialization logic
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public BeanOne beanOne() {
return new BeanOne();
}
@Bean(destroyMethod = "cleanup")
public BeanTwo beanTwo() {
return new BeanTwo();
}
}@PostConstruct注解实现:
public class CachingMovieLister {
@PostConstruct
public void populateMovieCache() {
// populates the movie cache upon initialization...
}
@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction...
}
}销毁回掉
实现org.springframework.beans.factory.DisposableBean接口的bean能够在bean销毁的时候被回掉:
public class CachingMovieLister {
@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction...
}
}同上推荐的方式是:
public class BeanTwo {
public void cleanup() {
// destruction logic
}
}
@Configuration
public class AppConfig {
@Bean(destroyMethod = "cleanup")
public BeanTwo beanTwo() {
return new BeanTwo();
}
}默认的初始化和销毁的回掉方法
当不使用InitializingBean和DisposableBean接口实现生命周期回掉时,可以使用init(), initialize(), dispose()等方法名定义回掉方法,通常来说这是标准的生命周期回掉方法名,并且所有的开发者都会使用这些方法名来保持统一性。
当使用init()来定义生命周期回掉时,不需要指定bean定义的init-method="init"属性。
所以初始化回掉就默认使用init(),销毁回掉使用dispose() :
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.");
}
}
}同时,可以通过init-method和destroy-method来覆盖默认的生命周期回掉
Spring容器能够保证一旦bean的所有依赖被注入就会立马回掉声明周期函方法,所以初始化回掉是发生在bean还没有被Spring Aop代理之前的时期。首先目标bean被完全创建,其次Aop再进行拦截。
组合生命周期回掉机制
Spring 2.5之后我们有3种选择来介入bean的生命周期:
InitializingBean和DisposableBean回掉方法- 自定义
init()和destroy()方法 @PostConstructand@PreDestroy注解.
并且我们可以组合使用这3种方式来介入bean的生命周期
注意:如果一个bean的生命周期被配置了多个回掉方法,那么这些回掉方法不能同名,并且将会按下面描述的顺序执行
Bean初始化回掉顺序:
@PostConstruct注解标注的方法最先执行InitializingBean的afterPropertiesSet()方法- 自定义的
init()方法
bean销毁回掉顺序同上:
@PreDestroy注解标注的方法最先执行DisposableBean的destroy()方法- 自定义的
destroy()方法
启动和停止回掉
Spring的Lifecycle接口为任何一个有自己的生命周期的bean定义了必要的方法,比如:正在启动和停止一些后任务
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}任何Spring管理的对象都可以实现Lifecycle接口,ApplicationContext在接收到启动和停止信号时,会将其传递给所有实现Lifecycle接口的对象,其内部时通过LifecycleProcessor来实现:
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onClose();
}LifecycleProcessor本身也继承了Lifecycle接口,并且添加了响应容器刷新和关闭的方法。
注意:
org.springframework.context.Lifecycle接口只是定义了启动的和停止的规范,但是并不能在上下文环境刷新时自动启动,如果需要实现自动启动,考虑实现org.springframework.context.SmartLifecycle接口更重要的是,
stop()方法的回掉不能保证在bean的销毁回掉之前被调用,在正常的停止过程中,实现Lifecycle的bean会首先收到容器停止的回掉其次是bean销毁的回掉,在容器热刷新等情况下,只会接收到bean销毁的回掉。
bean之间的启动和停止的调用顺序非常重要,如果2个bean之间有使用@dependsOn注解标识依赖关系,则依赖方会在被依赖方之后启动,在被依赖方之前停止,通常来说,bean之间的直接依赖关系是未知的,在这种情况下,SmartLifecycle接口定义了另一种方式来解决:其继承的父接口Phased的getPhase()方法。
public interface Phased {
int getPhase();
}SmartLifecycle接口:
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}当应用启动时,phase(阶段)值越低启动优先级越高,因此,如果一个实现了SmartLifecycle接口的对象的getPhase()方法返回值为Integer.MIN_VALUE将会成为最先启动的,最后停止的对象,反之亦然,没有实现SmartLifecycle的普通Lifecycle接口的phase值是0,因此任何负数phase值都将会优先于这些普通的对象创建。
SmartLifecycle定义的停止方法接收一个回掉函数,任何实现SmartLifecycle的对象都必须在应用停止的时候调用回掉函数的run()方法,DefaultLifecycleProcessor支持异步回掉停止方法,并且每个阶段的bean回掉超时默认是30s,可以通过自定义DefaultLifecycleProcessor进行修改:
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>LifecycleProcessor接口定义了在容器刷新和关闭时候的回掉方法,当所有的bean都实例化和初始化完后(即容器刷新)LifecycleProcessor会调用SmartLifecyclebean的isAutoStartup()方法,如果返回true,则SmartLifecyclebean的start()方法会被直接回掉,而不是等待显式的调用。
非web环境优雅关闭Spring ioC容器
如果在非web环境中使用Spring ioC容器,需要注册一个钩子到JVM上,这样能够确保调用所有单例bean的销毁方法使得所有资源被释放,所以必须正确的配置和实现这些销毁回掉函数。
调用ConfigurableApplicationContext的registerShutdownHook()方法注册钩子:
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Boot {
public static void main(final String[] args) throws Exception {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// add a shutdown hook for the above context...
ctx.registerShutdownHook();
// app runs here...
// main method exits, hook is called prior to the app shutting down...
}
}
