对于最新的稳定版本,请使用 Spring Framework 7.0.6!spring-doc.cadn.net.cn

自定义Bean的性质

Spring Framework 提供了许多接口,您可以使用它们来定制 bean 的性质。本节将其按如下方式分组:spring-doc.cadn.net.cn

生命周期回调

要与容器对bean生命周期的管理进行交互,可以实现Spring InitializingBeanDisposableBean接口。容器会调用afterPropertiesSet()用于前者,destroy()用于后者,以便在您的beans初始化和销毁时让bean执行某些操作。spring-doc.cadn.net.cn

JSR-250 @PostConstruct@PreDestroy 注解通常被认为是现代 Spring 应用中接收生命周期回调的最佳实践。使用这些注解意味着您的 Bean 不会与 Spring 特定的接口耦合。有关详细信息,请参阅 使用 @PostConstruct@PreDestroyspring-doc.cadn.net.cn

如果您不希望使用 JSR-250 注释但仍希望解耦,请考虑 init-methoddestroy-method 的 bean 定义元数据。spring-doc.cadn.net.cn

内部,Spring 框架使用 BeanPostProcessor 个实现来处理它能找到的任何回调接口并调用相应的方法。如果您需要 Spring 默认不提供的自定义功能或其他生命周期行为,您可以自己实现一个 BeanPostProcessor。有关更多信息,请参阅 容器扩展点spring-doc.cadn.net.cn

除了初始化和销毁回调之外,Spring管理的对象还可以实现 Lifecycle 接口,以便这些对象可以参与由容器自身生命周期驱动的启动和关闭过程。spring-doc.cadn.net.cn

生命周期回调接口在此部分中进行描述。spring-doc.cadn.net.cn

初始化回调

org.springframework.beans.factory.InitializingBean 接口允许在容器对 bean 设置所有必要属性之后,让 bean 执行初始化工作。InitializingBean 接口指定了一个方法:spring-doc.cadn.net.cn

void afterPropertiesSet() throws Exception;

我们建议不要使用 InitializingBean 接口,因为它不必要地将代码与 Spring 耦合。或者,我们建议使用 @PostConstruct 注解或指定一个 POJO 初始化方法。在基于 XML 的配置元数据情况下,可以使用 init-method 属性来指定一个具有 void 无参数签名的方法名称。对于 Java 配置,可以使用 initMethod 属性的 @Bean。参见 接收生命周期回调。考虑以下示例:spring-doc.cadn.net.cn

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {

	public void init() {
		// do some initialization work
	}
}
class ExampleBean {

	fun init() {
		// do some initialization work
	}
}

前面的示例与以下示例(由两个清单组成)几乎具有相同的效果:spring-doc.cadn.net.cn

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {

	@Override
	public void afterPropertiesSet() {
		// do some initialization work
	}
}
class AnotherExampleBean : InitializingBean {

	override fun afterPropertiesSet() {
		// do some initialization work
	}
}

然而,前面两个示例中的第一个并没有将代码与Spring耦合。spring-doc.cadn.net.cn

请注意,@PostConstruct 以及一般的初始化方法是在容器的单例创建锁内部执行的。在从@PostConstruct方法返回之后,才认为该bean实例完全初始化并准备好发布给其他对象。此类单独的初始化方法仅用于验证配置状态,并可能根据给定的配置准备一些数据结构,但不涉及与其他bean的进一步交互。否则可能会导致初始化死锁的风险。spring-doc.cadn.net.cn

在需要触发昂贵的后初始化操作的情况下,例如异步数据库准备步骤,您的Bean应实现 SmartInitializingSingleton.afterSingletonsInstantiated() 或依赖上下文 刷新事件:实现 ApplicationListener<ContextRefreshedEvent> 或 声明其注解等效项 @EventListener(ContextRefreshedEvent.class)。 这些变体在所有常规单例初始化之后执行,因此不在任何单例创建锁之内。spring-doc.cadn.net.cn

或者,您可以实现 (Smart)Lifecycle 接口,并与容器的整体生命周期管理集成,包括自动启动机制、预销毁停止步骤以及可能的停止/重启回调(见下文)。spring-doc.cadn.net.cn

销毁回调

实现 org.springframework.beans.factory.DisposableBean 接口可以让一个 bean 在包含它的容器被销毁时获得回调。 DisposableBean 接口指定一个方法:spring-doc.cadn.net.cn

void destroy() throws Exception;

我们建议您不要使用 DisposableBean 回调接口,因为它不必要的将代码与 Spring 耦合。或者,我们建议使用 @PreDestroy 注解或指定一个由 bean 定义支持的通用方法。使用基于 XML 的配置元数据时,可以在 <bean/> 上使用 destroy-method 属性。使用 Java 配置时,可以使用 @BeandestroyMethod 属性。参见 接收生命周期回调。考虑以下定义:spring-doc.cadn.net.cn

<bean id="exampleDestructionBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {

	public void cleanup() {
		// do some destruction work (like releasing pooled connections)
	}
}
class ExampleBean {

	fun cleanup() {
		// do some destruction work (like releasing pooled connections)
	}
}

前面的定义几乎与以下定义具有相同的效果:spring-doc.cadn.net.cn

<bean id="exampleDestructionBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {

	@Override
	public void destroy() {
		// do some destruction work (like releasing pooled connections)
	}
}
class AnotherExampleBean : DisposableBean {

	override fun destroy() {
		// do some destruction work (like releasing pooled connections)
	}
}

然而,前面两个定义中的第一个并没有将代码与Spring耦合。spring-doc.cadn.net.cn

请注意,Spring 还支持销毁方法的推断,会检测一个公共的 closeshutdown 方法。这是 Java 配置类中 @Bean 方法的默认行为,并会自动匹配 java.lang.AutoCloseablejava.io.Closeable 实现,从而不将销毁逻辑与 Spring 耦合。spring-doc.cadn.net.cn

对于使用XML的销毁方法推断,您可以将destroy-method元素的<bean>属性分配一个特殊的(inferred)值,这会指示Spring自动检测bean类上的公共closeshutdown方法,以用于特定的bean定义。您还可以将这个特殊的(inferred)值设置在default-destroy-method属性上,该属性属于<beans>元素,以将此行为应用于一组完整的bean定义(参见 默认的初始化和销毁方法)。

对于延长的关闭阶段,您可以实现 Lifecycle 接口,在任何单例 bean 的销毁方法被调用之前接收一个早期停止信号。 您还可以实现 SmartLifecycle 以进行时间限制的停止步骤,容器将在继续到销毁方法之前等待所有此类停止处理完成。spring-doc.cadn.net.cn

默认初始化和销毁方法

当你编写不使用Spring特定的InitializingBeanDisposableBean回调接口的初始化和销毁方法回调时,通常会编写诸如init()initialize()dispose()等名称的方法。理想情况下,这些生命周期回调方法的名称应在整个项目中标准化,以便所有开发人员使用相同的 方法名称并确保一致性。spring-doc.cadn.net.cn

您可以配置Spring容器以“查找”每个bean上的命名初始化和销毁回调方法名称。这意味着作为应用程序开发人员,您可以编写应用程序类并使用名为init()的初始化回调,而无需为每个bean定义配置init-method="init"属性。Spring IoC容器在创建bean时(并根据前面所述的标准生命周期回调约定描述)会调用该方法。此功能还对初始化和销毁方法回调强制执行一致的命名约定。spring-doc.cadn.net.cn

假设你的初始化回调方法命名为init(),而你的销毁回调方法命名为destroy()。那么你的类将类似于以下示例中的类:spring-doc.cadn.net.cn

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.");
		}
	}
}
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中:spring-doc.cadn.net.cn

<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 类具有此类方法,它会在适当的时候被调用。spring-doc.cadn.net.cn

您可以类似地配置销毁方法回调(在XML中,即)通过在顶级<beans/>元素上使用default-destroy-method属性。spring-doc.cadn.net.cn

如果现有的 bean 类已经具有与约定不同的命名的回调方法,可以通过使用 init-methoddestroy-method 属性在 XML 中指定方法名称来覆盖默认值。spring-doc.cadn.net.cn

Spring容器保证在bean获得所有依赖项之后立即调用配置的初始化回调。因此,初始化回调是在原始bean引用上调用的,这意味着AOP拦截器等尚未应用于该bean。首先完全创建目标bean,然后应用AOP代理(例如)及其拦截器链。如果目标bean和代理是单独定义的,您的代码甚至可以与原始目标bean交互,绕过代理。因此,对init方法应用拦截器将是不一致的,因为这样做会将目标bean的生命周期与其代理或拦截器耦合,并且当您的代码直接与原始目标bean交互时,会导致奇怪的语义。spring-doc.cadn.net.cn

结合生命周期机制

从 Spring 2.5 开始,您有三种选项可以控制 bean 的生命周期行为:spring-doc.cadn.net.cn

如果为一个 bean 配置了多种生命周期机制,并且每种机制都配置了不同的方法名称,那么在本注释之后列出的顺序中运行每个配置的方法。但是,如果同一方法名称被配置为这些生命周期机制中的多个机制 — 例如,初始化方法配置为init() — 则该方法只运行一次,如前面的章节所述。

对于同一个 bean 配置了多种生命周期机制,使用不同的初始化方法时,调用方式如下:spring-doc.cadn.net.cn

  1. 使用 @PostConstruct 注解的方法spring-doc.cadn.net.cn

  2. afterPropertiesSet()InitializingBean 回调接口定义spring-doc.cadn.net.cn

  3. 一个自定义配置的 init() 方法spring-doc.cadn.net.cn

销毁方法按相同的顺序被调用:spring-doc.cadn.net.cn

  1. 使用 @PreDestroy 注解的方法spring-doc.cadn.net.cn

  2. destroy()DisposableBean 回调接口定义spring-doc.cadn.net.cn

  3. 一个自定义配置的 destroy() 方法spring-doc.cadn.net.cn

启动和关闭回调

Lifecycle 接口定义了任何具有自己生命周期要求(例如启动和停止某些后台进程)的对象的基本方法:spring-doc.cadn.net.cn

public interface Lifecycle {

	void start();

	void stop();

	boolean isRunning();
}

任何由Spring管理的对象都可以实现Lifecycle接口。然后,当ApplicationContext本身接收到启动和停止信号(例如,在运行时的停止/重启场景中),它会将这些调用传递给该上下文中定义的所有Lifecycle实现。它是通过将这些调用委托给一个LifecycleProcessor来完成的,如以下清单所示:spring-doc.cadn.net.cn

public interface LifecycleProcessor extends Lifecycle {

	void onRefresh();

	void onClose();
}

请注意,LifecycleProcessor本身是Lifecycle接口的扩展。它还添加了另外两个方法,用于响应上下文被刷新和关闭的情况。spring-doc.cadn.net.cn

请注意,常规的 org.springframework.context.Lifecycle 接口是一个明确的启动和停止通知的契约,并不意味着在上下文刷新时自动启动。 为了对自动启动进行更精细的控制,并实现特定 bean 的优雅停止(包括启动和停止阶段),建议改用扩展的 org.springframework.context.SmartLifecycle 接口。spring-doc.cadn.net.cn

同时,请注意停止通知不一定在销毁之前出现。 在正常关闭时,所有 Lifecycle 作用域的 Bean 会先收到停止通知,然后才会传播通用的销毁回调。然而,在上下文生命周期内的热刷新或停止的刷新尝试时,只会调用销毁方法。spring-doc.cadn.net.cn

启动和关闭调用的顺序可能很重要。如果任意两个对象之间存在“depends-on”关系,依赖的一方会在其依赖项之后启动,并在其依赖项之前停止。然而,有时直接依赖关系是未知的。您可能只知道某种类型的对象应在另一种类型的对象之前启动。在这种情况下,SmartLifecycle 接口定义了另一种选项,即在其超接口 Phased 上定义的 getPhase() 方法。下面的列表显示了 Phased 接口的定义:spring-doc.cadn.net.cn

public interface Phased {

	int getPhase();
}

以下列表显示了 SmartLifecycle 接口的定义:spring-doc.cadn.net.cn

public interface SmartLifecycle extends Lifecycle, Phased {

	boolean isAutoStartup();

	void stop(Runnable callback);
}

在启动时,相位最低的对象会先启动。在停止时,会遵循相反的顺序。因此,一个实现了 SmartLifecycle 并且其 getPhase() 方法返回 Integer.MIN_VALUE 的对象会是最早启动的,并且最后停止。在另一方面,相位值为 Integer.MAX_VALUE 表示该对象应该最后启动,最先停止(可能因为它依赖于其他进程正在运行)。在考虑相位值时,还有一点很重要,即任何未实现 SmartLifecycle 的“正常”Lifecycle 对象的默认相位是 0。因此,任何负数相位值表示该对象应在标准组件之前启动(并在之后停止)。正数相位值则相反。spring-doc.cadn.net.cn

SmartLifecycle定义的stop方法接受一个回调。任何实现都必须在其关闭过程完成后调用该回调的run()方法。这使得在需要时可以进行异步关闭,因为LifecycleProcessor接口的默认实现DefaultLifecycleProcessor会在每个阶段中等待其对象组调用该回调,最多等待其超时值。每个阶段的默认超时时间为30秒。您可以通过在上下文中定义一个名为lifecycleProcessor的bean来覆盖默认的生命周期处理器实例。如果您只想修改超时时间,定义以下内容即可:spring-doc.cadn.net.cn

<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
	<!-- timeout value in milliseconds -->
	<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>

如前所述,LifecycleProcessor 接口为上下文的刷新和关闭定义了回调方法。后者会触发关闭过程,就像显式调用了 stop() 一样,但发生于上下文关闭时。另一方面,'refresh' 回调启用了 SmartLifecycle beans 的另一个功能。当上下文被刷新(在所有对象都被实例化和初始化之后)时,会调用该回调。此时,默认的生命周期处理器会检查每个 SmartLifecycle 对象的 isAutoStartup() 方法返回的布尔值。如果为 true,则会在该点启动该对象,而不是等待显式调用上下文或其自身的 start() 方法(与上下文刷新不同,标准上下文实现不会自动进行上下文启动)。phase 值和任何“depends-on”关系决定了如前所述的启动顺序。spring-doc.cadn.net.cn

在非Web应用程序中优雅地关闭Spring IoC容器

本节仅适用于非Web应用程序。Spring的基于Web的 ApplicationContext 实现已在适当的位置添加了代码,以便在相关Web应用程序关闭时优雅地关闭Spring IoC容器。spring-doc.cadn.net.cn

如果你在非Web应用环境中使用Spring的IoC容器(例如,在富客户端桌面环境中),请向JVM注册一个关闭钩子。这样可以确保优雅地关闭,并在单例bean上调用相关销毁方法,以便释放所有资源。你仍然需要正确地配置和实现这些销毁回调。spring-doc.cadn.net.cn

要注册一个关闭钩子,请调用在 registerShutdownHook() 接口上声明的 ConfigurableApplicationContext 方法,如下例所示:spring-doc.cadn.net.cn

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...
	}
}
import org.springframework.context.support.ClassPathXmlApplicationContext

fun main() {
	val ctx = 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...
}

ApplicationContextAwareBeanNameAware

ApplicationContext 创建一个实现 org.springframework.context.ApplicationContextAware 接口的对象实例时,该实例会获得对那个 ApplicationContext 的引用。下面的列表显示了 ApplicationContextAware 接口的定义:spring-doc.cadn.net.cn

public interface ApplicationContextAware {

	void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

因此,bean可以通过ApplicationContext接口或通过将引用强制转换为该接口的已知子类(如ConfigurableApplicationContext,它公开了额外的功能)来编程地操作创建它们的ApplicationContext。一个用途是程序化地检索其他bean。有时这个功能很有用。然而,通常应避免这样做,因为它会使代码与Spring耦合,并且不符合控制反转风格,其中协作对象作为属性提供给bean。ApplicationContext的其他方法提供了对文件资源的访问、发布应用事件和访问MessageSource的功能。这些附加功能在 ApplicationContext的其他功能中进行了描述。spring-doc.cadn.net.cn

自动注入是获取对ApplicationContext的引用的另一种方法。传统的constructorbyType自动注入模式(如< a t="C8">自动注入协作对象中所述)可以分别为构造函数参数或setter方法参数提供类型为36注解时,ApplicationContext将被自动注入到期望ApplicationContext类型的字段、构造函数参数或方法参数中。有关更多信息,请参见< a t="C9">使用@Autowiredspring-doc.cadn.net.cn

ApplicationContext 创建一个实现 org.springframework.beans.factory.BeanNameAware 接口的类时,该类会获得对其相关对象定义中定义的名称的引用。下面的列表显示了 BeanNameAware 接口的定义:spring-doc.cadn.net.cn

public interface BeanNameAware {

	void setBeanName(String name) throws BeansException;
}

回调在正常 bean 属性填充之后被调用,但在初始化回调(如 InitializingBean.afterPropertiesSet() 或自定义的 init-method)之前。spring-doc.cadn.net.cn

其他 Aware 接口

除了 ApplicationContextAwareBeanNameAware(在前面讨论过), Spring 提供了广泛的 Aware 回调接口,让 beans 可以向容器表明它们需要某种基础架构依赖。一般来说,名称表示依赖类型。下表总结了最重要的 Aware 接口:spring-doc.cadn.net.cn

表1. Aware接口
名称 注入的依赖项 解释在……

ApplicationContextAwarespring-doc.cadn.net.cn

声明 ApplicationContextspring-doc.cadn.net.cn

ApplicationContextAwareBeanNameAwarespring-doc.cadn.net.cn

ApplicationEventPublisherAwarespring-doc.cadn.net.cn

封闭的 ApplicationContext 的事件发布者。spring-doc.cadn.net.cn

Spring框架的其他功能spring-doc.cadn.net.cn

BeanClassLoaderAwarespring-doc.cadn.net.cn

用于加载 bean 类的类加载器。spring-doc.cadn.net.cn

实例化Beanspring-doc.cadn.net.cn

BeanFactoryAwarespring-doc.cadn.net.cn

声明 BeanFactoryspring-doc.cadn.net.cn

The BeanFactory APIspring-doc.cadn.net.cn

BeanNameAwarespring-doc.cadn.net.cn

声明该 bean 的名称。spring-doc.cadn.net.cn

ApplicationContextAwareBeanNameAwarespring-doc.cadn.net.cn

LoadTimeWeaverAwarespring-doc.cadn.net.cn

定义用于在加载时处理类定义的织入器。spring-doc.cadn.net.cn

Spring框架中使用AspectJ的运行时织入spring-doc.cadn.net.cn

MessageSourceAwarespring-doc.cadn.net.cn

用于解析消息的配置策略(支持参数化和国际化)。spring-doc.cadn.net.cn

Spring框架的其他功能spring-doc.cadn.net.cn

NotificationPublisherAwarespring-doc.cadn.net.cn

Spring JMX 通知发布器。spring-doc.cadn.net.cn

通知spring-doc.cadn.net.cn

ResourceLoaderAwarespring-doc.cadn.net.cn

用于低级别访问资源的配置加载器。spring-doc.cadn.net.cn

资源spring-doc.cadn.net.cn

ServletConfigAwarespring-doc.cadn.net.cn

当前 ServletConfig 容器运行的环境。仅在具有网络感知能力的 Spring 中有效 ApplicationContext.spring-doc.cadn.net.cn

Spring MVCspring-doc.cadn.net.cn

ServletContextAwarespring-doc.cadn.net.cn

当前 ServletContext 容器运行的环境。仅在具有网络感知能力的 Spring 中有效 ApplicationContext.spring-doc.cadn.net.cn

Spring MVCspring-doc.cadn.net.cn

再次提醒,使用这些接口会将你的代码与 Spring API 绑定,并不遵循控制反转风格。因此,我们建议它们用于需要程序化访问容器的基础架构 Bean。spring-doc.cadn.net.cn