version 5.2.25.RELEASE
目录
1.6.2.ApplicationContextAware和BeanNameAware
1.6.自定义Bean的性质
Spring Framework提供了许多接口,可以通过这些接口来自定义bean的性质。本节将它们分组如下:
- 生命周期回调
- 应用程序上下文感知和Bean名称感知
- 其他感知接口
1.6.1.生命周期回调
实现容器对bean生命周期的交互管理,可以通过实现Spring框架的 InitializingBean和DisposableBean接口方式。容器为前者调用afterPropertiesSet(),为后者调用destroy(),让bean在初始化和销毁bean时执行某些操作。
JSR-250@PostConstruct和@PreDestroy注释通常被认为是在现代Spring应用程序中接收生命周期回调的最佳实践。使用这些注释意味着您的bean没有耦合到Spring特定的接口。有关详细信息,请参见使用@PostConstruct和@PreDestroy。
如果您不想使用JSR-250注释,但仍想删除耦合,请考虑init方法和destroy方法bean定义元数据。
在内部,Spring Framework使用BeanPostProcessor实现来处理它可以找到的任何回调接口,并调用适当的方法。如果您需要自定义功能或Spring默认不提供的其他生命周期行为,您可以自己实现BeanPostProcessor。有关详细信息,请参见容器扩展点。
除了初始化和销毁回调之外,Spring管理的对象还可以实现Lifecycle接口,以便这些对象可以参与由容器自己的生命周期驱动的启动和关闭过程。
本节介绍了生命周期回调接口。
初始化回调
需实现org.springframework.beans.factory.InitializingBean接口。
InitializingBean接口的方法:
void afterPropertiesSet() throws Exception;
推荐实现方式:
- 基于XML文件配置元数据实现接口的方式:配置bean的init-method属性
- 通过Java配置方式:使用@Bean的initMethod属性<>
- 使用@PostConstruct注释或指定POJO初始化方法
不推荐方式:
- 直接使用InitializingBean接口
以下为基于XML配置元数据实现接口方式的示例:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
Java
public class ExampleBean {
public void init() {
// do some initialization work
}
}
Kotlin
class ExampleBean {
fun init() {
// do some initialization work
}
}
不推荐方式示例:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
Java
public class AnotherExampleBean implements InitializingBean {
@Override
public void afterPropertiesSet() {
// do some initialization work
}
}
Kotlin
class AnotherExampleBean : InitializingBean {
override fun afterPropertiesSet() {
// do some initialization work
}
}
销毁回调
需要实现org.springframework.beans.factory.DisposableBean接口;
DisposableBean接口指定了一个方法:
void destroy() throws Exception;
推荐实现方式:
- 基于XML文件配置元数据实现DisposableBean接口:配置bean的destroy-method属性
- 通过Java配置方式:使用@Bean的destroyMethod属性<>
- 使用@PreDestroy注释或指定bean定义支持的泛型方法
不推荐方式:
- 直接使用DisposableBean接口
以下为基于XML配置元数据实现接口方式的示例:
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
Java
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
Kotlin
class ExampleBean {
fun cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
不推荐方式示例:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
Java
public class AnotherExampleBean implements DisposableBean {
@Override
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
Kotlin
class AnotherExampleBean : DisposableBean {
override fun destroy() {
// do some destruction work (like releasing pooled connections)
}
}
您可以为<bean>元素的destroy-method属性分配一个特殊(推断)值,该值指示Spring自动检测特定bean类上的公共关闭或关闭方法。(因此,任何实现java.lang.AutoCloseable或java.io.Closable的类都会匹配。)您还可以在<beans>元素的默认destroy-method属性上设置此特殊(推断)值,以将此行为应用于整个bean集(请参阅默认初始化和销毁方法)。请注意,这是Java配置的默认行为。
默认初始化和销毁方法
不使用Spring特定InitializingBean和DisposableBean回调接口,要实现类实例的初始化和销毁方法回调时:
- 为每个类编写初始化和销毁方法回调强制执行一致的命名约定,如名称为init()、initialize()、dispose()等的方法
- XML配置需配置Beans的初始化和销毁时的默认属性,如default-init-method
- Spring IoC容器在创建bean通过自动调用相应行法
以下为示例:
假设初始化回调方法的名称为init(),销毁回调方法的名字为destroy()
Java
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.");
}
}
}
Kotlin
class DefaultBlogService : BlogService {
private var blogDao: BlogDao? = null
// this is (unsurprisingly) the initialization callback method
fun init() {
if (blogDao == null) {
throw IllegalStateException("The [blogDao] property must be set.")
}
}
}
然后,您可以在类似以下内容的bean中使用该类:
<beans default-init-method="init">
<bean id="blogService" class="com.something.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
顶级<beans/>元素属性上的default-init-method方法属性会导致Spring IoC容器将bean类上名为init的方法识别为初始化方法回调。当创建和组装bean时,如果bean类有这样的方法,则会在适当的时候调用它。
通过在顶级<beans/>元素上使用的default-destroy-method属性,可以类似地(在XML中)配置默认销毁方法回调。
如果现有的bean类已经具有与约定不一致的回调方法,则可以通过使用<bean/>本身的init方法和destroy方法属性指定(在XML中)方法名称来覆盖默认值。
Spring容器保证在向bean提供所有依赖项后立即调用已配置的初始化回调。因此,初始化回调是在原始bean引用上调用的,这意味着AOP拦截器等尚未应用于bean。首先完全创建一个目标bean,然后应用带有其拦截器链的AOP代理(例如)。如果目标bean和代理是单独定义的,那么代码甚至可以绕过代理与原始目标bean进行交互。因此,将拦截器应用于init方法是不一致的,因为这样做会将目标bean的生命周期与其代理或拦截器耦合,并在代码直接与原始目标bean交互时留下奇怪的语义。
组合生命周期机制
从Spring 2.5开始,您有三个选项来控制bean的生命周期行为:
- InitializingBean和DisposableBean回调接口
- Custom init()和destroy()方法
- @PostConstruct和@PreDestroy注释。
您可以组合这些机制来控制给定的bean。
为同一个bean配置的多个生命周期机制,使用不同的初始化方法,调用如下:
- 用InitializingBean回调接口定义的@postConstruct
- afterPropertiesSet()注释的方法
- 自定义配置的init()方法
销毁方法的调用顺序相同:
- 用DisposableBean回调接口定义的@PreDestroy
- destroy()注释的方法
- 自定义配置的destroy
启动和关闭回调
生命周期接口定义了具有自身生命周期要求的任何对象的基本方法(例如启动和停止某些后台进程):
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
任何Spring管理的对象都可以实现Lifecycle接口。然后,当ApplicationContext本身接收到启动和停止信号时,它将这些调用级联到该上下文中定义的所有生命回收机制实现。它通过委派给LifecycleProcessor来实现这一点,如以下列表所示:
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onClose();
}
请注意,LifecycleProcessor本身就是Lifecycle接口的扩展。它还添加了另外两种方法,用于对刷新和关闭的上下文做出反应。
请注意,常规的org.springframework.context.Lifecycle接口是用于显式启动和停止通知的普通约定,并不意味着在上下文刷新时自动启动。为了对特定bean的自动启动(包括启动阶段)进行细粒度控制,请考虑实现org.springframework.context.SmartLifecycle。
此外,请注意,停止通知不能保证在销毁之前发出。在定期关闭时,所有Lifecycle Bean都会在传播常规销毁回调之前首先收到停止通知。但是,在上下文生存期内的热刷新或停止刷新尝试时,只调用destroy方法。
启动和关闭调用的顺序可能很重要。如果任何两个对象之间存在“依赖-依赖”关系,则依赖方从其依赖项之后开始,在其依赖项之前停止。然而,有时,直接依赖关系是未知的。您可能只知道某个类型的对象应该在另一个类型的物体之前开始。在这些情况下,SmartLifecycle接口定义了另一个选项,即在其超级接口Phased上定义的getPhase()方法。以下列表显示了分阶段接口的定义:
public interface Phased {
int getPhase();
}
以下列表显示了SmartLifecycle接口的定义:
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
启动时,相位最低的对象首先启动。停止时,遵循相反的顺序。因此,实现SmartLifecycle并且其getPhase()方法返回Integer.MIN_VALUE的对象将是最先启动和最后停止的对象之一。在光谱的另一端,Integer.MAX_value的相位值将指示对象应该最后启动,首先停止(可能是因为它取决于要运行的其他进程)。在考虑阶段值时,同样重要的是要知道,任何未实现SmartLifecycle的“正常”生命周期对象的默认阶段都是0。因此,任何负相位值都表示对象应在这些标准零部件之前开始(并在它们之后停止)。对于任何正相位值,情况正好相反。
SmartLifecycle定义的停止方法接受回调。任何实现都必须在该实现的关闭过程完成后调用该回调的run()方法。这在必要时启用异步关闭,因为LifecycleProcessor接口的默认实现DefaultLifecyclePprocessor会等待每个阶段中的对象组调用该回调,直到其超时值。每个阶段的默认超时为30秒。您可以通过在上下文中定义一个名为lifecycleProcessor的bean来覆盖默认的生命周期处理器实例。如果您只想修改超时,定义以下内容就足够了:
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
如前所述,LifecycleProcessor接口还定义了刷新和关闭上下文的回调方法。后者驱动关闭进程,就好像显式调用了stop()一样,但它发生在上下文关闭时。另一方面,“刷新”回调启用了SmartLifecycle bean的另一项功能。当刷新上下文时(在所有对象都被实例化和初始化之后),会调用该回调。此时,默认生命周期处理器会检查每个SmartLifecycle对象的isAutoStartup()方法返回的布尔值。如果为true,则该对象将在此时启动,而不是等待上下文或其自身的start()方法的显式调用(与上下文刷新不同,对于标准上下文实现,上下文启动不会自动发生)。相位值和任何“依赖”关系决定了启动顺序,如前所述。
在非Web应用程序中优雅地关闭Spring IoC容器
本节仅适用于非web应用程序。
方法:向JVM注册关闭挂钩,这样做可以确保优雅的关闭,并在单例bean上调用相关的destroy方法,从而释放所有资源。
实现方式:要注册关机挂钩,请调用在ConfigurationApplicationContext接口上声明的registerShutdownHook()方法,如下例所示:
Java
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...
}
}
1.6.2.ApplicationContextAware和BeanNameAware
当ApplicationContext创建一个实现org.springframework.context.ApplicationContextAware接口的对象实例时,会为该实例提供对该ApplicationContext的引用。以下列表显示了ApplicationContextAware接口的定义:
public interface ApplicationContextAware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
因此,bean可以通过ApplicationContext接口或通过将引用强制转换为此接口的已知子类(如ConfigurationApplicationContext,它公开了额外的功能),以编程方式操作创建它们的ApplicationContext。一种用途是对其他bean进行编程检索。有时这种能力是有用的。然而,通常情况下,您应该避免它,因为它将代码与Spring耦合,并且不遵循反向控制风格,在反向控制风格中,协作者作为属性提供给bean。ApplicationContext的其他方法提供对文件资源的访问、发布应用程序事件和访问MessageSource。ApplicationContext的附加功能中介绍了这些附加功能。
自动连线是获取ApplicationContext引用的另一种选择。传统的构造函数和byType自动连接模式(如autowiring Collaborators中所述)可以分别为构造函数参数或setter方法参数提供ApplicationContext类型的依赖关系。为了获得更大的灵活性,包括自动关联字段和多参数方法的能力,请使用基于注释的自动关联功能。如果这样做,ApplicationContext将自动连接到字段、构造函数参数或方法参数中,如果有问题的字段、构造函数或方法带有@autowired注释,则该字段、构造函数自变量或方法参数需要ApplicationContext类型。有关详细信息,请参见使用@Autowired。
ApplicationContext创建实现org.springframework.beans.factory.BeanNameAware接口的类时,会为该类提供对其关联对象定义中定义的名称的引用。以下列表显示BeanNameAware接口的定义:
public interface BeanNameAware {
void setBeanName(String name) throws BeansException;
}
回调是在填充普通bean属性之后调用的,但在初始化回调(如InitializingBean.afterPropertiesSet()或自定义init方法)之前调用。
1.6.3.其他Aware接口
下表总结了最重要的Aware接口:
名称 | 注入的依赖项 | 在以下章节解析 |
---|---|---|
| Declaring | |
| Event publisher of the enclosing | |
| Class loader used to load the bean classes. | |
| Declaring | |
| Name of the declaring bean. | |
| Resource adapter | |
| Defined weaver for processing class definition at load time. | |
| Configured strategy for resolving messages (with support for parametrization and internationalization). | |
| Spring JMX notification publisher. | |
| Configured loader for low-level access to resources. | |
| Current | |
| Current |