1.6 定制化Bean
Spring框架提供了许多接口,您可以使用它们来对bean进行定制化。本节将它们分组如下:
1.6.1 生命周期回调(Lifecycle Callbacks)
为了容器与bean生命周期管理进行交互,您可以实现Spring的InitializingBean和DisposableBean接口。容器对前者调用afterPropertiesSet(),对后者调用destroy(),让bean在初始化和销毁时执行某些操作。
JSR-250 @PostConstruct和@PreDestroy注解通常被认为是现代Spring应用程序中接收生命周期回调的最佳实践。使用这些注解意味着您的bean没有与特定的spring接口进行耦合。详细信息请参见使用@PostConstruct和@PreDestroy docs.spring.io/spring-fram…
如果您不想使用JSR-250注释,但仍然希望删除耦合,可以考虑init-method和destroy-method。
在内部,Spring框架使用BeanPostProcessor实现来处理它能找到的任何回调接口,并调用适当的方法。如果您需要自定义特性或Spring默认不提供的其他生命周期行为,您可以自己实现BeanPostProcessor。有关更多信息,请参见 Container Extension Points. docs.spring.io/spring-fram…
除了初始化和销毁回调,spring管理的对象也可以实现Lifecycle接口,以便这些对象可以参与容器启动和关闭过程
本节描述生命周期回调接口。
初始化回调(Initialization Callbacks)
initializingbean接口允许bean在容器设置完bean所有必要属性后执行初始化工作。InitializingBean接口指定了一个方法:
void afterPropertiesSet() throws Exception;
复制代码
我们建议您不要使用InitializingBean接口,因为它不必要地将代码耦合到Spring。另外,我们建议使用@PostConstruct注释或指定POJO初始化方法。对于基于xml的配置元数据,可以使用init-method属性指定空参数的方法为初始化方法。基于Java配置,您可以使用@Bean的initMethod属性。 Receiving Lifecycle Callbacks。考虑以下例子:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
复制代码
public class ExampleBean {
public void init() {
// do some initialization work
} }
复制代码
上面的示例与下面的示例效果几乎完全相同:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
复制代码
public class AnotherExampleBean implements InitializingBean {
@Override
public void afterPropertiesSet() {
// do some initialization work
}
}
复制代码
但是,前面两个示例中的第一个没有将代码与Spring耦合。
销毁回调方法(Destruction Callbacks)
实现org.springframework. factory. disposablebean接口可以让bean在容器销毁它时获得一个回调。DisposableBean接口指定了一个方法:
void destroy() throws Exception;
复制代码
我们建议您不要使用DisposableBean回调接口,因为它不必要地将代码与Spring耦合在一起。另外,我们建议使用@PreDestroy注释或使用基于xml的配置元数据,使用destroy-method属性指定回调方法。在Java配置中,可以使用@Bean的destroyMethod属性。参考以下定义:
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
复制代码
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
复制代码
上面的定义与下面的定义几乎完全相同:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
复制代码
public class AnotherExampleBean implements DisposableBean {
@Override
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
复制代码
但是,前面两个示例中的第一个没有将代码与Spring耦合。
默认的初始化、 销毁回调方法
当您不使用特定于spring的InitializingBean和DisposableBean回调接口的初始化和销毁方法回调时,您通常会编写init()、initialize()、dispose()等名称的方法。理想情况下,生命周期回调方法的名称在整个项目中是标准化的,以便所有开发人员使用相同的方法名称并确保一致性。
您可以将Spring容器配置“查找”已命名初始化或销毁的回调方法。这意味着,作为应用程序开发人员,您可以编写应用程序类并使用名为init()的初始化回调函数,而不必为每个bean定义配置init-method=”init”属性。Spring IoC容器在创建bean时调用该方法(并且按照前面描述的标准生命周期回调契约)。
假设初始化回调方法名为init(),销毁回调方法名为destroy()。你的类就像下面例子中的类:
`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.");
}
}
复制代码
然后你可以像下面这样在bean中使用这个类:
<bean id="blogService" class="com.something.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
复制代码
顶级元素属性上的default-init-method属性导致Spring IoC容器将bean类上名为init的方法识别为初始化方法回调。在创建和组装一个bean时,如果这个bean类有这样的方法,就会在适当的时候调用它。
通过在顶级元素上使用default-destroy-method属性,您可以类似地配置销毁方法回调。
如果现有的bean类已经有了与约定不同的回调方法,那么您可以通过使用本身的init-method和destroy-method属性指定方法名来覆盖默认值。
Spring容器保证在装载了包含所有依赖项的bean之后立即调用配置好的初始化回调。
复合生命周期机制
从spring2.5开始,你有三个选项来控制bean的生命周期行为:
- 实现
InitializingBean
和DisposableBean
回调接口 - 自定义
init()
和destroy()
方法 @PostConstruct
and@PreDestroy
注解.
你可以复合使用这些机制。
如果为一个bean配置了多个生命周期机制,并且每个机制都配置了不同的方法名,那么每个已配置的方法将按照本文后面列出的顺序运行。但是,如果为多个生命周期机制配置了相同的方法名——例如,为一个初始化方法配置init(),则该方法将运行一次
为同一个bean配置的具有不同初始化方法的多个生命周期机制的调用如下:
-
调用用@PostConstruct注释的方法
-
调用InitializingBean回调接口定义的afterPropertiesSet()
-
调用自定义配置的init()方法
销毁方法也遵循以下顺序:
- 调用
@PreDestroy
注释的方法 - 调用
DisposableBean
回调接口定义的destroy()
方法 - 自定义
destroy()
方法
Startup 、 Shutdown 回调方法
Lifecyccle接口定义了任何有自己生命周期需求的对象的基本方法(例如启动和停止某些后台进程):
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接口是显式启动和停止通知的简单约定,并不意味着在上下文刷新时自动启动。对于特定bean的自动启动(包括启动阶段)的细粒度控制,可以考虑实现org.springframework.context.SmartLifecycle。
另外,请注意,停止通知不能保证在销毁之前出现。在常规关闭时,在传播常规销毁回调之前,所有Lifecycle bean首先收到一个停止通知。但是,在上下文生命周期内的热刷新或停止刷新尝试时,只调用destroy销毁方法。
启动和关闭调用的顺序可能很重要。如果任意两个对象之间存在“依赖”关系,依赖方在其依赖项之后启动,在其依赖项之前停止。然而,有时,直接依赖关系是未知的。您可能只知道某种类型的对象应该在另一种类型的对象之前启动。在这些情况下,SmartLifecycle接口定义了另一个选项,即在其父接口phase上定义的getPhase()方法。phase接口的定义如下所示:
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将指示对象应该最后启动,首先停止(可能是因为它依赖于其他正在运行的进程)。当考虑阶段值时,同样重要的是要知道任何“正常”生命周期对象的默认阶段是0,但它没有实现SmartLifecycle。因此,任何负相位值都表明对象应该在这些标准组件之前开始(并在它们之后停止)。对于任何正相位值,则相反。
SmartLifecycle定义的stop方法接受回调。任何实现都必须在实现的shutdown()完成后调用回调的run()方法。这使得异步关闭成为可能,因为LifecycleProcessor接口的默认实现DefaultLifecycleProcessor将等待每个阶段中的对象组的超时值来调用该回调。每个阶段的默认超时时间是30秒。您可以通过在上下文中定义一个名为lifecycleProcessor的bean来覆盖默认的生命周期处理器实例。如果你只想修改超时时间,定义以下内容就足够了:
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
复制代码
如前所述,LifecycleProcessor接口还定义了用于刷新(onrefresh)和关闭(onclose)上下文的回调方法。后者驱动关机进程,就像显式调用了stop()一样,但它发生在上下文关闭时。另一方面,’refresh’回调启用了SmartLifecycle bean的另一个特性。当上下文被刷新时(在所有对象被实例化和初始化之后),该回调将被调用。此时,默认的生命周期处理器会检查每个SmartLifecycle对象的isAutoStartup()方法返回的布尔值。如果为true,则该对象将在此点启动,而不是等待上下文的显式调用或它自己的start()方法(不同于上下文刷新,对于标准上下文实现,上下文启动不会自动发生)。如前所述,parse值和任何“依赖”关系决定启动顺序。
在非web应用程序中安全地关闭Spring IoC容器
本节仅适用于非web应用。Spring基于web的ApplicationContext实现如上所述,可以在相关web应用程序关闭时优雅地关闭Spring IoC容器。
如果您在非web应用程序环境中使用Spring的IoC容器(例如,在客户端桌面环境中),请向JVM注册一个关机挂钩。这样做可以确保正常关闭,并调用单例bean上的相关destroy方法,以便释放所有资源。您仍然必须正确配置和实现这些销毁回调。
要注册一个shutdown钩子,调用在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...
}
复制代码
}`
1.6.2. ApplicationContextAware
and BeanNameAware
当ApplicationContext创建一个实现org.springframework.context.ApplicationContextAware接口的对象实例时,该实例会提供ApplicationContext的引用。下面代码显示了ApplicationContextAware接口的定义:
public interface ApplicationContextAware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
复制代码
因此,bean可以通过程序操作创建它们的ApplicationContext,通过ApplicationContext接口,或者通过将引用强转成该接口的已知子类(例如提供了额外功能的ConfigurableApplicationContext)。一种用途是对其他bean进行检索。有时这种能力是有用的,但是,通常应该避免使用它,因为它将代码耦合到Spring,并且不遵循控制反转(Inversion of Control),在这种样式中协作者作为属性提供给bean。ApplicationContext的其他方法提供了对文件资源的访问、发布应用程序事件和访问MessageSource。这些附加特性在ApplicationContext的附加功能中进行了描述。
自动装配是另一种获取ApplicationContext引用的方法。传统的构造函数和byType自动装配模式(如autowiring合作者所述)可以分别为构造函数参数或setter方法参数提供ApplicationContext类型的依赖关系。为了获得更多的灵活性,包括自动装配字段和多个参数方法的能力,可以使用基于注释的自动装配特性。如果您这样做,ApplicationContext将自动连接到一个字段、构造函数参数或方法参数,如果相关字段、构造函数或方法带有@Autowired注释,则该字段、构造函数或方法参数需要为ApplicationContext类型。有关更多信息,请参见使用@Autowired。
当ApplicationContext创建一个实现org.springframework.beans.factory.BeanNameAware接口的类时,会为该类提供一个对对象定义中定义的名称。下面的代码显示了BeanNameAware接口的定义:
public interface BeanNameAware {
void setBeanName(String name) throws BeansException;
}
复制代码
回调在填充普通bean属性之后,但在初始化回调(如InitializingBean、propertiesset之后或自定义初始化方法之前)之前被调用。
1.6.3. Other Aware Interfaces
除了ApplicationContextAware和BeanNameAware(前面讨论过)之外,Spring还提供了一系列Aware回调接口,让bean向容器表明它们需要的特定的依赖项。作为一般规则,Aware前的名称表示依赖项。下表总结了最重要的Aware接口:
Name | Injected Dependency | Explained in… |
---|---|---|
ApplicationContextAware |
Declaring ApplicationContext . |
ApplicationContextAware and BeanNameAware |
ApplicationEventPublisherAware |
Event publisher of the enclosing ApplicationContext . |
Additional Capabilities of the ApplicationContext |
BeanClassLoaderAware |
Class loader used to load the bean classes. | Instantiating Beans |
BeanFactoryAware |
Declaring BeanFactory . |
ApplicationContextAware and BeanNameAware |
BeanNameAware |
Name of the declaring bean. | ApplicationContextAware and BeanNameAware |
LoadTimeWeaverAware |
Defined weaver for processing class definition at load time. | Load-time Weaving with AspectJ in the Spring Framework |
MessageSourceAware |
Configured strategy for resolving messages (with support for parametrization and internationalization). | Additional Capabilities of the ApplicationContext |
NotificationPublisherAware |
Spring JMX notification publisher. | Notifications |
ResourceLoaderAware |
Configured loader for low-level access to resources. | Resources |
ServletConfigAware |
Current ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext . |
Spring MVC |
ServletContextAware |
Current ServletContext the container runs in. Valid only in a web-aware Spring ApplicationContext . |
Spring MVC |