此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10spring-doc.cadn.net.cn

自定义 Bean 的性质

Spring Framework 提供了许多接口,您可以使用它来自定义性质 豆子的。本节按如下方式对它们进行分组:spring-doc.cadn.net.cn

生命周期回调

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

The JSR-250@PostConstruct@PreDestroy注释通常被认为是最好的 在现代 Spring 应用程序中接收生命周期回调的练习。使用这些 注释意味着您的 bean 未耦合到特定于 Spring 的接口。 有关详细信息,请参阅@PostConstruct@PreDestroy.spring-doc.cadn.net.cn

如果您不想使用 JSR-250 注释,但仍想删除 耦合,考虑init-methoddestroy-methodbean 定义元数据。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 在容器在 豆。这InitializingBeaninterface 指定了单个方法: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并且通常执行初始化方法 在容器的单例创建锁中。仅考虑 bean 实例 作为完全初始化的,并准备好在从@PostConstruct方法。此类单独的初始化方法仅用于 用于验证配置状态并可能准备一些数据结构 基于给定的配置,但没有外部 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 在包含它的容器被销毁时获得回调。 这DisposableBeaninterface 指定了单个方法:spring-doc.cadn.net.cn

void destroy() throws Exception;

我们建议您不要使用DisposableBeancallback 接口,因为它不必要地将代码耦合到 Spring。或者,我们建议使用 这@PreDestroy注释或指定 Bean 定义支持的泛型方法。对于基于 XML 的配置元数据,您可以使用destroy-method属性<bean/>. 在 Java 配置中,您可以使用destroyMethod属性@Bean. 请参阅接收生命周期回调。请考虑以下定义: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 还支持对 destroy 方法的推断,检测 publiccloseshutdown方法。这是@BeanJava 配置中的方法 类并自动匹配java.lang.AutoCloseablejava.io.Closeable实现,也没有将销毁逻辑耦合到 Spring。spring-doc.cadn.net.cn

对于使用 XML 进行销毁方法推理,您可以分配destroy-method属性 的<bean>元素 a 特殊(inferred)value,它指示 Spring 自动 检测公共closeshutdown特定 bean 定义的 bean 类上的方法。 您还可以设置此特殊(inferred)default-destroy-method属性 的<beans>元素将此行为应用于整组 Bean 定义(请参阅默认初始化和销毁方法)。

对于延长的关机阶段,您可以实现Lifecycle接口和接收 在调用任何单例 Bean 的 destroy 方法之前的提前停止信号。 您也可以实现SmartLifecycle对于有时间限制的停止步骤,其中容器 将等待所有此类停止处理完成,然后再继续销毁方法。spring-doc.cadn.net.cn

默认初始化和销毁方法

当您编写初始化和销毁方法回调时,不使用 弹簧专用InitializingBeanDisposableBean回调接口,您 通常使用以下名称编写方法init(),initialize(),dispose(), 等等。理想情况下,此类生命周期回调方法的名称在各个领域都是标准化的 一个项目,以便所有开发人员使用相同的方法名称并确保一致性。spring-doc.cadn.net.cn

您可以将 Spring 容器配置为“查找”命名初始化并销毁 每个 bean 上的回调方法名称。这意味着,作为应用程序开发人员,您, 可以编写应用程序类并使用名为init(), 无需配置init-method="init"属性。 Spring IoC 容器在创建 bean 时调用该方法(并根据 与前面描述的标准生命周期回调合约)。 此功能还强制执行一致的初始化命名约定,并且 destroy 方法回调。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>

的存在default-init-method属性<beans/>元素 属性导致 Spring IoC 容器识别一个名为init在豆子上 class 作为初始化方法回调。创建和组装 Bean 时,如果 bean 类有这样的方法,它在适当的时候被调用。spring-doc.cadn.net.cn

您可以使用default-destroy-method属性<beans/>元素。spring-doc.cadn.net.cn

现有 bean 类已经具有以差异命名的回调方法 使用约定时,您可以通过指定(即在 XML 中)的 方法 name 的init-methoddestroy-method属性<bean/>本身。spring-doc.cadn.net.cn

Spring 容器保证调用配置的初始化回调 立即提供所有依赖项的 bean。因此,初始化 callback 在原始 bean 引用上调用,这意味着 AOP 拦截器等 第四个尚未应用于 bean。首先完全创建目标 Bean,并且 然后应用 AOP 代理(例如)及其拦截器链。如果目标 bean 和代理是单独定义的,你的代码甚至可以与原始 target 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. @PostConstructspring-doc.cadn.net.cn

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

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

Destroy 方法的调用顺序相同:spring-doc.cadn.net.cn

  1. @PreDestroyspring-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

另外,请注意,不能保证在销毁之前会收到停止通知。 在定期关闭时,所有Lifecyclebean 首先收到停止通知,然后 常规销毁回调正在传播中。但是,在热刷新期间 上下文的生存期或停止的刷新尝试时,仅调用 destroy 方法。spring-doc.cadn.net.cn

启动和关闭调用的顺序可能很重要。如果“依赖” 关系存在于任意两个对象之间,则依赖端在其之后开始 依赖关系,并且它在依赖关系之前停止。然而,有时,直接 依赖关系未知。您可能只知道某种类型的对象应该启动 在另一种类型的对象之前。在这些情况下,SmartLifecycle接口定义 另一个选项,即getPhase()在其超级接口上定义的方法,Phased.以下列表显示了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将指示对象应最后启动并停止 首先(可能是因为它依赖于其他进程的运行)。在考虑 相位值,同样重要的是要知道任何“正常”的默认相位Lifecycle未实现的对象SmartLifecycle0.因此,任何 负相位值表示对象应在这些标准之前启动 组件(并在它们之后停止)。对于任何正相位值,情况正相关。spring-doc.cadn.net.cn

SmartLifecycle接受回调。任何 实现必须调用该回调的run()方法,然后是该实现的 关闭过程完成。这在必要时启用异步关闭,因为 默认实现的LifecycleProcessor接口DefaultLifecycleProcessor,等待对象组的超时值 在每个阶段中调用该回调。默认的每阶段超时为 30 秒。 您可以通过定义名为lifecycleProcessor在上下文中。如果只想修改超时, 定义以下内容就足够了: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豆。刷新上下文时(在所有对象都已刷新后) 实例化并初始化),调用该回调。此时, 默认生命周期处理器检查每个SmartLifecycle对象的isAutoStartup()方法。如果true,则该对象是 从那时开始,而不是等待显式调用上下文的 or 自己start()方法(与上下文刷新不同,上下文启动不会发生 自动用于标准上下文实现)。这phasevalue 和任何 “依赖”关系确定启动顺序,如前所述。spring-doc.cadn.net.cn

在非 Web 应用程序中正常关闭 Spring IoC 容器

本节仅适用于非 Web 应用程序。Spring 的基于 Web 的ApplicationContext实现已经有代码可以正常关闭 Spring IoC 容器,当相关 Web 应用程序关闭时。spring-doc.cadn.net.cn

如果您在非 Web 应用程序环境中使用 Spring 的 IoC 容器(对于 例如,在富客户端桌面环境中),向 JVM.这样做可以确保正常关闭,并在 singleton bean 的 bean,以便释放所有资源。您仍必须配置 并正确实现这些 destroy 回调。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...
}

线程安全性和可见性

Spring 核心容器以线程安全的方式发布创建的单例实例, 通过单例锁保护访问并保证在其他线程中的可见性。spring-doc.cadn.net.cn

因此,应用程序提供的 bean 类不必担心 其初始化状态的可见性。常规配置字段不必是 标记为volatile只要它们只是在初始化阶段发生突变, 提供类似于final甚至对于基于 setter 的配置 在初始阶段可变的状态。如果此类字段在 bean 创建阶段及其随后的初始发布,它们需要声明为volatile或在访问时由公共锁保护。spring-doc.cadn.net.cn

请注意,在单例 Bean 实例中并发访问此类配置状态 例如,对于控制器实例或存储库实例,在 从容器端进行如此安全的初始发布。这包括常见的单例FactoryBean在常规单例锁中处理的实例。spring-doc.cadn.net.cn

对于销毁回调,配置状态保持线程安全,但任何运行时 初始化和销毁之间累积的状态应保持线程安全 结构(或在volatile字段)根据常见的 Java 准则。spring-doc.cadn.net.cn

更深Lifecycle集成,如上所示涉及运行时可变状态,例如 一个runnable字段,必须声明为volatile.虽然常见的 生命周期回调遵循一定的顺序,例如,保证启动回调 仅在完全初始化后发生,并且仅在初始启动后才停止回调, 销毁前的共同停止有一个特殊情况:它很强 建议任何此类 bean 中的内部状态也允许立即 destroy 回调而不预先停止,因为这可能发生在特殊情况下 在取消引导后关闭,或者在另一个 Bean 导致停止超时的情况下关闭。spring-doc.cadn.net.cn

ApplicationContextAwareBeanNameAware

ApplicationContext创建一个对象实例,该实例实现了org.springframework.context.ApplicationContextAware接口,则提供实例 并参考了这一点ApplicationContext.以下列表显示了定义 的ApplicationContextAware接口:spring-doc.cadn.net.cn

public interface ApplicationContextAware {

	void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

因此,bean 可以以编程方式作ApplicationContext创造了他们, 通过ApplicationContext接口或通过将引用转换为已知的 该接口的子类(例如ConfigurableApplicationContext,这会暴露 附加功能)。一种用途是以编程方式检索其他 bean。 有时此功能很有用。但是,一般来说,您应该避免它,因为 它将代码耦合到 Spring,并且不遵循控制反转样式, 其中协作者作为属性提供给 bean。的其他方法ApplicationContext提供对文件资源的访问、发布应用程序事件、 并访问MessageSource.这些附加功能在的附加功能ApplicationContext.spring-doc.cadn.net.cn

自动布线是获取对ApplicationContext.传统的 constructorbyType自动接线模式 (如自动布线协作器中所述)可以提供类型ApplicationContext对于构造函数参数或 setter 方法参数, 分别。为了获得更大的灵活性,包括自动布线字段和 多种参数方法,使用基于注释的自动布线功能。如果你这样做, 这ApplicationContext自动连接到字段、构造函数参数或方法 参数,该参数需要ApplicationContext如果字段、构造函数或 有问题的方法携带@Autowired注解。有关更多信息,请参阅@Autowired.spring-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-方法。spring-doc.cadn.net.cn

其他Aware接口

此外ApplicationContextAwareBeanNameAware前面讨论过), Spring 提供广泛的Aware让 bean 向容器指示的回调接口 它们需要一定的基础设施依赖性。作为一般规则,名称表示 依赖类型。下表总结了最重要的Aware接口:spring-doc.cadn.net.cn

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

ApplicationContextAwarespring-doc.cadn.net.cn

声明ApplicationContext.spring-doc.cadn.net.cn

ApplicationContextAwareBeanNameAwarespring-doc.cadn.net.cn

ApplicationEventPublisherAwarespring-doc.cadn.net.cn

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

的附加功能ApplicationContextspring-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

声明BeanFactory.spring-doc.cadn.net.cn

BeanFactory应用程序接口spring-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

的附加功能ApplicationContextspring-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容器运行。仅在 Web 感知 Spring 中有效ApplicationContext.spring-doc.cadn.net.cn

弹簧 MVCspring-doc.cadn.net.cn

ServletContextAwarespring-doc.cadn.net.cn

当前ServletContext容器运行。仅在 Web 感知 Spring 中有效ApplicationContext.spring-doc.cadn.net.cn

弹簧 MVCspring-doc.cadn.net.cn

再次请注意,使用这些接口会将您的代码绑定到 Spring API,而不是 遵循控制反转样式。因此,我们建议将它们用于基础设施 需要以编程方式访问容器的 Bean。spring-doc.cadn.net.cn