如何定制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()
方法 @PostConstruct
and@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
会调用SmartLifecycle
bean的isAutoStartup()
方法,如果返回true
,则SmartLifecycle
bean的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...
}
}