1.6. Customizing the nature of a bean
1.6. 自定义一个bean的本质
The Spring Framework provides a number of interfaces you can use to customize the nature of a bean. This section groups them as follows:
Spring框架提供了许多接口,可用于自定义Bean的性质。 本节将它们分组如下:
- Lifecycle Callbacks
- 生命周期回调
- ApplicationContextAware and BeanNameAware
- ApplicationContextAware和BeanNameAware
- Other Aware Interfaces
- 其他感知接口
1.6.1. Lifecycle callbacks
1.6.1. 生命周期回调
To interact with the container’s management of the bean lifecycle, you can implement the Spring InitializingBean and DisposableBean interfaces. The container calls afterPropertiesSet() for the former and destroy() for the latter to allow the bean to perform certain actions upon initialization and destruction of your beans.
要与bean生命周期的容器管理进行交互,可以实现Spring InitializingBean和DisposableBean接口。 容器为前者调用afterPropertiesSet(),为后者调用destroy()以允许bean在初始化和销毁bean时执行某些操作。
The JSR-250 @PostConstruct and @PreDestroy annotations are generally considered best practice for receiving lifecycle callbacks in a modern Spring application. Using these annotations means that your beans are not coupled to Spring specific interfaces. For details see @PostConstruct and @PreDestroy.
JSR-250 @PostConstruct和**@PreDestroy注释通常被认为是在现代Spring应用程序中接收生命周期回调的最佳实践。 使用这些注释意味着您的bean不会耦合到Spring特定的接口。 有关详细信息,请参阅@PostConstruct和@PreDestroy**。
If you don’t want to use the JSR-250 annotations but you are still looking to remove coupling consider the use of init-method and destroy-method object definition metadata.
如果您不想使用JSR-250注释但仍希望删除耦合,请考虑使用init-method和destroy-method对象定义元数据。
Internally, the Spring Framework uses BeanPostProcessor implementations to process any callback interfaces it can find and call the appropriate methods. If you need custom features or other lifecycle behavior Spring does not by default offer, you can implement a BeanPostProcessor yourself. For more information, see Container Extension Points.
在内部,Spring Framework使用BeanPostProcessor实现来处理它可以找到的任何回调接口并调用适当的方法。 如果您需要Spring默认提供的自定义功能或其他生命周期行为,您可以自己实现BeanPostProcessor。 有关更多信息,请参阅容器扩展点。
In addition to the initialization and destruction callbacks, Spring-managed objects may also implement the Lifecycle interface so that those objects can participate in the startup and shutdown process, as driven by the container’s own lifecycle.
除了初始化和销毁回调之外,Spring管理的对象还可以实现Lifecycle接口,以便这些对象可以参与启动和关闭过程,这是由容器自身的生命周期驱动的。
The lifecycle callback interfaces are described in this section.
本节描述了生命周期回调接口。
Initialization Callbacks
初始化回调
The org.springframework.beans.factory.InitializingBean interface lets a bean perform initialization work after the container has set all necessary properties on the bean. The InitializingBean interface specifies a single method:
org.springframework.beans.factory.InitializingBean接口允许bean在容器已在bean上设置所有必需属性后执行初始化工作。 InitializingBean接口指定一个方法:
void afterPropertiesSet() throws Exception;
We recommend that you do not use the InitializingBean interface, because it unnecessarily couples the code to Spring. Alternatively, we suggest using the @PostConstruct annotation or specifying a POJO initialization method. In the case of XML-based configuration metadata, you can use the init-method attribute to specify the name of the method that has a void no-argument signature. With Java configuration, you can use the initMethod attribute of @Bean. See Receiving Lifecycle Callbacks. Consider the following example:
我们建议您不要使用InitializingBean接口,因为它会不必要地将代码耦合到Spring。 或者,我们建议使用@PostConstruct注释或指定POJO初始化方法。 对于基于XML的配置元数据,可以使用init-method属性指定具有void无参数签名的方法的名称。 使用Java配置,您可以使用@Bean的initMethod属性。 请参阅接收生命周期回调。 请考虑以下示例:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// do some initialization work
}
}
The preceding example has almost exactly the same effect as the following example (which consists of two listings):
前面的示例与以下示例(由两个列表组成)具有几乎完全相同的效果:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}
However, the second of the two preceding examples does not couple the code to Spring.
但是,前面两个示例中的第二个没有将代码耦合到Spring。
Destruction Callbacks
毁灭回调
Implementing the org.springframework.beans.factory.DisposableBean interface lets a bean get a callback when the container that contains it is destroyed. The DisposableBean interface specifies a single method:
实现org.springframework.beans.factory.DisposableBean接口允许bean在包含它的容器被销毁时获得回调。 DisposableBean接口指定一个方法:
void destroy() throws Exception;
We recommend that you do not use the DisposableBean callback interface, because it unnecessarily couples the code to Spring. Alternatively, we suggest using the @PreDestroy annotation or specifying a generic method that is supported by bean definitions. With XML-based configuration metadata, you can use the destroy-method attribute on the . With Java configuration, you can use the destroyMethod attribute of @Bean. See Receiving Lifecycle Callbacks. Consider the following definition:
我们建议您不要使用DisposableBean回调接口,因为它会不必要地将代码耦合到Spring。 或者,我们建议使用@PreDestroy注释或指定bean定义支持的泛型方法。 使用基于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)
}
}
The preceding definition has almost exactly the same effect as the following definition:
前面的定义与以下定义几乎完全相同:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
However, the second of the two preceding definitions does not couple the code to Spring.
但是,前面两个定义中的第二个没有将代码耦合到Spring。
You can assign the destroy-method attribute of a element a special (inferred) value, which instructs Spring to automatically detect a public close or shutdown method on the specific bean class. (Any class that implements java.lang.AutoCloseable or java.io.Closeable would therefore match.) You can also set this special (inferred) value on the default-destroy-method attribute of a element to apply this behavior to an entire set of beans (see Default Initialization and Destroy Methods). Note that this is the default behavior with Java configuration.
您可以为元素的destroy-method属性分配一个特殊的(推断的)值,该值指示Spring自动检测特定bean类的公共关闭或关闭方法。 (因此,任何实现java.lang.AutoCloseable或java.io.Closeable的类都将匹配。)您还可以在元素的default-destroy-method属性上设置此特殊(推断)值,以将此行为应用于一整套bean(参见默认初始化和销毁方法)。请注意,这是Java配置的默认行为。
Default Initialization and Destroy Methods
默认初始化和销毁方法
When you write initialization and destroy method callbacks that do not use the Spring-specific InitializingBean and DisposableBean callback interfaces, you typically write methods with names such as init(), initialize(), dispose(), and so on. Ideally, the names of such lifecycle callback methods are standardized across a project so that all developers use the same method names and ensure consistency.
当您编写初始化和销毁不使用特定于Spring的InitializingBean和DisposableBean回调接口的方法回调时,通常会编写名称为init(),initialize(),dispose()等的方法。理想情况下,此类生命周期回调方法的名称在项目中是标准化的,以便所有开发人员使用相同的方法名称并确保一致性。
You can configure the Spring container to “look” for named initialization and destroy callback method names on every bean. This means that you, as an application developer, can write your application classes and use an initialization callback called init(), without having to configure an init-method=“init” attribute with each bean definition. The Spring IoC container calls that method when the bean is created (and in accordance with the standard lifecycle callback contract described previously). This feature also enforces a consistent naming convention for initialization and destroy method callbacks.
您可以将Spring容器配置为“查找”命名初始化并销毁每个bean上的回调方法名称。 这意味着,作为应用程序开发人员,您可以编写应用程序类并使用名为init()的初始化回调,而无需为每个bean定义配置init-method =“init”属性。 Spring IoC容器在创建bean时调用该方法(并且符合前面描述的标准生命周期回调契约)。 此功能还强制执行初始化和销毁方法回调的一致命名约定。
Suppose that your initialization callback methods are named init() and your destroy callback methods are named destroy(). Your class then resembles the class in the following example:
假设您的初始化回调方法名为init(),而您的destroy回调方法名为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.");
}
}
}
You could then use that class in a bean resembling the following:
然后,您可以在类似于以下内容的bean中使用该类:
<beans default-init-method="init">
<bean id="blogService" class="com.something.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
The presence of the default-init-method attribute on the top-level element attribute causes the Spring IoC container to recognize a method called init on the bean class as the initialization method callback. When a bean is created and assembled, if the bean class has such a method, it is invoked at the appropriate time.
顶级元素属性上存在default-init-method属性会导致Spring IoC容器将bean类上的init方法识别为初始化方法回调。当bean被创建和组装时,如果bean类具有这样的方法,则在适当的时候调用它。
You can configure destroy method callbacks similarly (in XML, that is) by using the default-destroy-method attribute on the top-level element.
您可以使用顶级元素上的default-destroy-method属性,以类似方式(在XML中)配置destroy方法回调。
Where existing bean classes already have callback methods that are named at variance with the convention, you can override the default by specifying (in XML, that is) the method name by using the init-method and destroy-method attributes of the itself.
如果现有的Bean类已经具有按约定命名的回调方法,则可以通过使用的init-method和destroy-method属性指定(在XML中)方法名称来覆盖默认值 本身。
The Spring container guarantees that a configured initialization callback is called immediately after a bean is supplied with all dependencies. Thus, the initialization callback is called on the raw bean reference, which means that AOP interceptors and so forth are not yet applied to the bean. A target bean is fully created first and then an AOP proxy (for example) with its interceptor chain is applied. If the target bean and the proxy are defined separately, your code can even interact with the raw target bean, bypassing the proxy. Hence, it would be inconsistent to apply the interceptors to the init method, because doing so would couple the lifecycle of the target bean to its proxy or interceptors and leave strange semantics when your code interacts directly with the raw target bean.
Spring容器保证在为bean提供所有依赖项后立即调用配置的初始化回调。因此,在原始bean引用上调用了初始化回调,这意味着AOP拦截器等尚未应用于bean。首先完全创建目标bean,然后应用带有其拦截器链的AOP代理(例如)。如果目标Bean和代理分别定义,则您的代码甚至可以绕过代理与原始目标Bean进行交互。因此,将拦截器应用于init方法将是不一致的,因为这样做会将目标Bean的生命周期耦合到其代理或拦截器,并且当您的代码直接与原始目标Bean进行交互时会留下奇怪的语义。
Combining Lifecycle Mechanisms
组合生命周期机制
As of Spring 2.5, you have three options for controlling bean lifecycle behavior:
从Spring 2.5开始,您可以使用三个选项来控制Bean生命周期行为:
- The InitializingBean and DisposableBean callback interfaces
- InitializingBean和DisposableBean回调接口
- Custom init() and destroy() methods
- 自定义init()和destroy()方法
- The @PostConstruct and @PreDestroy annotations. You can combine these mechanisms to control a given bean.
- @PostConstruct和@PreDestroy批注。 您可以结合使用这些机制来控制给定的bean。
If multiple lifecycle mechanisms are configured for a bean and each mechanism is configured with a different method name, then each configured method is executed in the order listed after this note. However, if the same method name is configured — for example, init() for an initialization method — for more than one of these lifecycle mechanisms, that method is executed once, as explained in the preceding section.
如果为一个bean配置了多个生命周期机制,并且为每个机制配置了不同的方法名称,则将按照此注释后列出的顺序执行每个已配置的方法。 但是,如果为多个生命周期机制中的多个生命周期机制配置了相同的方法名称(例如,为初始化方法使用init()),则该方法将执行一次,如上一节所述。
Multiple lifecycle mechanisms configured for the same bean, with different initialization methods, are called as follows:
为同一个bean配置的具有不同初始化方法的多种生命周期机制如下:
- Methods annotated with @PostConstruct
- afterPropertiesSet() as defined by the InitializingBean callbackinterface
- A custom configured init() method定制配置的init()方法
Destroy methods are called in the same order:
销毁方法的调用顺序相同:
- Methods annotated with @PreDestroy
- destroy() as defined by the DisposableBean callback interface
- A custom configured destroy() method
Startup and Shutdown Callbacks
启动和关机回调
The Lifecycle interface defines the essential methods for any object that has its own lifecycle requirements (such as starting and stopping some background process):
Lifecycle接口为具有自己的生命周期要求(例如启动和停止某些后台进程)的任何对象定义了基本方法:
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
Any Spring-managed object may implement the Lifecycle interface. Then, when the ApplicationContext itself receives start and stop signals (for example, for a stop/restart scenario at runtime), it cascades those calls to all Lifecycle implementations defined within that context. It does this by delegating to a LifecycleProcessor, shown in the following listing:
任何Spring管理的对象都可以实现Lifecycle接口。 然后,当ApplicationContext本身接收到启动和停止信号时(例如,对于运行时的停止/重新启动方案),它将把这些调用级联到在该上下文中定义的所有Lifecycle实现。 它通过委派给LifecycleProcessor来做到这一点,如以下清单所示:
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onClose();
}
Notice that the LifecycleProcessor is itself an extension of the Lifecycle interface. It also adds two other methods for reacting to the context being refreshed and closed.
注意 LifecycleProcessor 本身就是 Lifecycle 接口的扩展。它还添加了另外两种方法来对正在刷新和关闭的上下文做出反应。
Note that the regular org.springframework.context.Lifecycle interface is a plain contract for explicit start and stop notifications and does not imply auto-startup at context refresh time. For fine-grained control over auto-startup of a specific bean (including startup phases), consider implementing org.springframework.context.SmartLifecycle instead.
请注意,常规 org.springframework.context.Lifecycle 接口是显式启动和停止通知的普通合同,并不意味着在上下文刷新时自动启动。要对特定 bean 的自动启动(包括启动阶段)进行细粒度控制,请考虑实施 org.springframework.context.SmartLifecycle。
Also, please note that stop notifications are not guaranteed to come before destruction. On regular shutdown, all Lifecycle beans first receive a stop notification before the general destruction callbacks are being propagated. However, on hot refresh during a context’s lifetime or on aborted refresh attempts, only destroy methods are called.
另外,请注意,停止通知不能保证在销毁之前发出。在定期关闭时,所有 Lifecycle bean 首先会在传播一般销毁回调之前收到停止通知。但是,在上下文的生命周期内进行热刷新或中止刷新尝试时,只会调用销毁方法。
The order of startup and shutdown invocations can be important. If a “depends-on” relationship exists between any two objects, the dependent side starts after its dependency, and it stops before its dependency. However, at times, the direct dependencies are unknown. You may only know that objects of a certain type should start prior to objects of another type. In those cases, the SmartLifecycle interface defines another option, namely the getPhase() method as defined on its super-interface, Phased. The following listing shows the definition of the Phased interface:
启动和关闭调用的顺序可能很重要。如果任何两个对象之间存在“依赖”关系,则依赖方在其依赖之后开始,并在其依赖之前停止。但是,有时,直接依赖关系是未知的。您可能只知道某种类型的对象应该先于另一种类型的对象开始。在这些情况下,SmartLifecycle 接口定义了另一个选项,即在其超级接口 Phased 上定义的 getPhase() 方法。以下清单显示了 Phased 接口的定义:
public interface Phased {
int getPhase();
}
The following listing shows the definition of the SmartLifecycle interface:
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
When starting, the objects with the lowest phase start first. When stopping, the reverse order is followed. Therefore, an object that implements SmartLifecycle and whose getPhase() method returns Integer.MIN_VALUE would be among the first to start and the last to stop. At the other end of the spectrum, a phase value of Integer.MAX_VALUE would indicate that the object should be started last and stopped first (likely because it depends on other processes to be running). When considering the phase value, it is also important to know that the default phase for any “normal” Lifecycle object that does not implement SmartLifecycle is 0. Therefore, any negative phase value indicates that an object should start before those standard components (and stop after them). The reverse is true for any positive phase value.
启动时,相位最低的对象首先启动。停止时,按照相反的顺序。因此,实现 SmartLifecycle 并且其 getPhase() 方法返回 Integer.MIN_VALUE 的对象将是最先启动和最后停止的对象。在频谱的另一端,Integer.MAX_VALUE 的相位值表示对象应该最后启动并首先停止(可能是因为它依赖于正在运行的其他进程)。在考虑阶段值时,知道任何未实现 SmartLifecycle 的“正常”生命周期对象的默认阶段是 0 也很重要。因此,任何负阶段值表示对象应该在那些标准组件之前启动(并停止在他们之后)。对于任何正相位值,反之亦然。
The stop method defined by SmartLifecycle accepts a callback. Any implementation must invoke that callback’s run() method after that implementation’s shutdown process is complete. That enables asynchronous shutdown where necessary, since the default implementation of the LifecycleProcessor interface, DefaultLifecycleProcessor, waits up to its timeout value for the group of objects within each phase to invoke that callback. The default per-phase timeout is 30 seconds. You can override the default lifecycle processor instance by defining a bean named lifecycleProcessor within the context. If you want only to modify the timeout, defining the following would suffice:
SmartLifecycle 定义的 stop 方法接受回调。任何实现都必须在该实现的关闭过程完成后调用该回调的 run() 方法。这会在必要时启用异步关闭,因为 LifecycleProcessor 接口的默认实现 DefaultLifecycleProcessor 会等待每个阶段内的对象组调用该回调的超时值。默认的每阶段超时为 30 秒。您可以通过在上下文中定义一个名为生命周期处理器的 bean 来覆盖默认的生命周期处理器实例。如果您只想修改超时,定义以下内容就足够了:
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
As mentioned earlier, the LifecycleProcessor interface defines callback methods for the refreshing and closing of the context as well. The latter drives the shutdown process as if stop() had been called explicitly, but it happens when the context is closing. The ‘refresh’ callback, on the other hand, enables another feature of SmartLifecycle beans. When the context is refreshed (after all objects have been instantiated and initialized), that callback is invoked. At that point, the default lifecycle processor checks the boolean value returned by each SmartLifecycle object’s isAutoStartup() method. If true, that object is started at that point rather than waiting for an explicit invocation of the context’s or its own start() method (unlike the context refresh, the context start does not happen automatically for a standard context implementation). The phase value and any “depends-on” relationships determine the startup order as described earlier.
如前所述,LifecycleProcessor 接口还定义了用于刷新和关闭上下文的回调方法。后者驱动关闭过程,就好像 stop() 已被显式调用,但它发生在上下文关闭时。另一方面,“刷新”回调启用了 SmartLifecycle bean 的另一个功能。刷新上下文时(在所有对象都已实例化和初始化之后),将调用该回调。此时,默认生命周期处理器会检查每个 SmartLifecycle 对象的 isAutoStartup() 方法返回的布尔值。如果为 true,则该对象在该点启动,而不是等待上下文或其自己的 start() 方法的显式调用(与上下文刷新不同,对于标准上下文实现,上下文启动不会自动发生)。如前所述,阶段值和任何“依赖”关系决定了启动顺序。
Shutting Down the Spring IoC Container Gracefully in Non-web Applications
在非 Web 应用程序中优雅地关闭 Spring IoC 容器
This section applies only to non-web applications. Spring’s web-based ApplicationContext implementations already have code in place to gracefully shut down the Spring IoC container when the relevant web application is shut down.
本节仅适用于非 Web 应用程序。 Spring 的基于 Web 的 ApplicationContext 实现已经有代码可以在相关 Web 应用程序关闭时优雅地关闭 Spring IoC 容器。
If you use Spring’s IoC container in a non-web application environment (for example, in a rich client desktop environment), register a shutdown hook with the JVM. Doing so ensures a graceful shutdown and calls the relevant destroy methods on your singleton beans so that all resources are released. You must still configure and implement these destroy callbacks correctly.
如果您在非 Web 应用程序环境中使用 Spring 的 IoC 容器(例如,在富客户端桌面环境中),请向 JVM 注册一个关闭挂钩。 这样做可确保正常关闭并在单例 bean 上调用相关的销毁方法,以便释放所有资源。 您仍然必须正确配置和实现这些销毁回调。
To register a shutdown hook, call the registerShutdownHook() method that is declared on the ConfigurableApplicationContext interface, as the following example shows:
要注册关闭挂钩,请调用在 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...
}
}