自定义 Bean 的性质
Spring Framework 提供了许多接口,您可以使用这些接口来自定义 豆子。本节将它们分组如下:
生命周期回调
要与容器对 bean 生命周期的 Management 进行交互,您可以实现
Spring 和接口。容器调用前者,而后者则让 bean
在初始化和销毁 bean 时执行某些操作。InitializingBean
DisposableBean
afterPropertiesSet()
destroy()
JSR-250 和注解通常被认为是最好的
在现代 Spring 应用程序中接收生命周期回调的实践。使用这些
annotations 意味着你的 bean 没有耦合到特定于 Spring 的接口。
有关详细信息,请参阅使用 如果您不想使用 JSR-250 注解,但仍希望删除
coupling、consider 和 bean 定义元数据。 |
在内部, Spring 框架使用 implementation 来处理任何
callback 接口,它可以找到并调用适当的方法。如果您需要自定义
功能或其他生命周期行为 Spring 默认不提供,您可以
实施 YOURSELF。有关更多信息,请参阅容器扩展点。BeanPostProcessor
BeanPostProcessor
除了初始化和销毁回调之外, Spring Management 的对象还可以
此外,实现接口,以便这些对象可以参与
启动和关闭过程,由容器自身的生命周期驱动。Lifecycle
生命周期回调接口将在 此部分 中介绍。
初始化回调
该接口允许 bean
在容器在
豆。该接口指定单个方法:org.springframework.beans.factory.InitializingBean
InitializingBean
void afterPropertiesSet() throws Exception;
我们建议您不要使用该接口,因为它
不必要地将代码耦合到 Spring。或者,我们建议使用
@PostConstruct
注解或
指定 POJO 初始化方法。对于基于 XML 的配置元数据,
您可以使用该属性指定具有 void 的方法的名称
no-argument 签名。对于 Java 配置,您可以使用 .请参阅 接收生命周期回调。请考虑以下示例:InitializingBean
init-method
initMethod
@Bean
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
-
Java
-
Kotlin
public class ExampleBean {
public void init() {
// do some initialization work
}
}
class ExampleBean {
fun init() {
// do some initialization work
}
}
前面的示例与以下示例的效果几乎完全相同 (由两个列表组成):
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
-
Java
-
Kotlin
public class AnotherExampleBean implements InitializingBean {
@Override
public void afterPropertiesSet() {
// do some initialization work
}
}
class AnotherExampleBean : InitializingBean {
override fun afterPropertiesSet() {
// do some initialization work
}
}
但是,前面两个示例中的第一个示例并没有将代码耦合到 Spring。
请注意,通常会执行 and 初始化方法
在容器的单例创建锁中。仅考虑 bean 实例
已完全初始化,并准备好在从方法返回后发布给其他人。这种单独的初始化方法仅意味着
用于验证配置状态并可能准备一些数据结构
基于给定的配置,但没有进一步的外部 Bean 访问活动。
否则,存在初始化死锁的风险。 对于要触发昂贵的初始化后活动的场景,
例如,异步数据库准备步骤中,您的 bean 应该实现或依赖于上下文
refresh 事件:implementation 或
声明其 Comments 等效 。
这些变体出现在所有常规单例初始化之后,因此
在任何单例创建锁之外。 或者,您可以实现接口并与
容器的整体生命周期管理,包括自动启动机制,
销毁前停止步骤,以及可能的停止/重启回调(见下文)。 |
销毁回调
实现该接口后,可以让
bean 在包含它的容器被销毁时获取回调。该接口指定单个方法:org.springframework.beans.factory.DisposableBean
DisposableBean
void destroy() throws Exception;
我们建议您不要使用回调接口,因为它
不必要地将代码耦合到 Spring。或者,我们建议使用
@PreDestroy
注解或
指定 Bean 定义支持的泛型方法。使用基于 XML 的
configuration 元数据中,您可以使用 .
对于 Java 配置,您可以使用 .请参阅 接收生命周期回调。请考虑以下定义:DisposableBean
destroy-method
<bean/>
destroyMethod
@Bean
<bean id="exampleDestructionBean" class="examples.ExampleBean" destroy-method="cleanup"/>
-
Java
-
Kotlin
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)
}
}
前面的定义与下面的定义几乎完全相同:
<bean id="exampleDestructionBean" class="examples.AnotherExampleBean"/>
-
Java
-
Kotlin
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 还支持销毁方法的推理,检测 public 或 method。这是 Java 配置中方法的默认行为
类并自动匹配或实现,也不会将销毁逻辑耦合到 Spring。close
shutdown
@Bean
java.lang.AutoCloseable
java.io.Closeable
对于使用 XML 的 destroy 方法推理,您可以分配属性
元素中,一个特殊值,它指示 Spring 自动将
检测特定 Bean 定义的 Bean 类上的 public 或 method。
您还可以在属性上设置此特殊值
将此行为应用于整个 bean 定义集(请参见默认初始化和销毁方法)。destroy-method <bean> (inferred) close shutdown (inferred) default-destroy-method <beans> |
对于扩展的关闭阶段,您可以实现接口并接收
在调用任何 singleton bean 的 destroy 方法之前提前停止信号。
您还可以实现有时限的 stop 步骤,其中容器
将等待所有此类 stop 处理完成,然后再继续销毁方法。 |
默认 Initialization 和 Destroy 方法
当您编写不使用
特定于 Spring 的接口和回调接口,您
通常编写名称为 、
等等。理想情况下,此类生命周期回调方法的名称在
一个项目,以便所有开发人员使用相同的方法名称并确保一致性。InitializingBean
DisposableBean
init()
initialize()
dispose()
你可以将 Spring 容器配置为“查找”命名初始化和销毁
回调方法名称。这意味着,作为应用程序开发人员,
可以编写您的应用程序类并使用名为
而不必为每个 bean 定义配置一个属性。
Spring IoC 容器在创建 bean 时调用该方法(并根据
使用前面描述的标准生命周期回调协定)。
此功能还对初始化和
destroy 方法回调。init()
init-method="init"
假设您的初始化回调方法已命名,并且您的 destroy
回调方法命名为 。然后,您的类类似于
以下示例:init()
destroy()
-
Java
-
Kotlin
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 中使用该类:
<beans default-init-method="init">
<bean id="blogService" class="com.something.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
顶级元素上是否存在该属性
属性使 Spring IoC 容器识别在 Bean 上调用的方法
class 作为初始化方法回调。在创建和组装 bean 时,如果
Bean 类具有这样一个方法,则会在适当的时间调用它。default-init-method
<beans/>
init
您可以通过使用 top-level 元素上的属性以类似方式(即在 XML 中)配置 destroy 方法回调。default-destroy-method
<beans/>
现有 Bean 类已经具有以 Variance 命名的回调方法
使用约定,您可以通过指定(在 XML 中,即)来覆盖默认值
method name 来使用本身的 and 属性。init-method
destroy-method
<bean/>
Spring 容器保证调用配置的初始化回调
紧接着为 bean 提供所有依赖项。因此,初始化
callback 在原始 bean 引用上调用,这意味着 AOP 拦截器等
forth 尚未应用于 Bean。首先完全创建目标 Bean,然后
然后应用 AOP 代理(例如)及其拦截器链。如果目标
bean 和 proxy 是单独定义的,你的代码甚至可以与原始的
target bean,绕过代理。因此,应用
interceptor 添加到该方法中,因为这样做会耦合
将 bean 绑定到其代理或拦截器,并在您的代码
直接与原始目标 Bean 交互。init
组合生命周期机制
从 Spring 2.5 开始,您有三个选项来控制 bean 生命周期行为:
-
InitializingBean
和DisposableBean
回调接口 -
自定义和方法
init()
destroy()
-
@PostConstruct
和@PreDestroy
注释-
您可以组合这些机制来控制给定的 bean。
-
如果为 Bean 配置了多个生命周期机制,并且每个机制都是
配置了不同的方法名称,则每个配置的方法都会在
在此说明之后列出的顺序。但是,如果为这些生命周期机制中的多个配置了相同的方法名称(例如,对于初始化方法),则
该方法运行一次,如上一节所述。init() |
为同一 bean 配置了多个生命周期机制,具有不同的 初始化方法的调用方式如下:
-
注释有
@PostConstruct
-
afterPropertiesSet()
由回调接口定义InitializingBean
-
自定义配置的方法
init()
Destroy 方法的调用顺序相同:
-
注释有
@PreDestroy
-
destroy()
由回调接口定义DisposableBean
-
自定义配置的方法
destroy()
启动和关闭回调
该接口为任何具有自己的
生命周期要求(例如启动和停止某些后台进程):Lifecycle
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
任何 Spring Management 的对象都可以实现该接口。然后,当 本身收到启动和停止信号(例如,对于停止/重启
scenario 的 intent 中),它会将这些调用级联到所有实现
在该上下文中定义。它通过委托给 , 所示
在下面的清单中:Lifecycle
ApplicationContext
Lifecycle
LifecycleProcessor
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onClose();
}
请注意,它本身是接口的扩展。它还添加了另外两种方法来响应正在刷新的上下文
并关闭。LifecycleProcessor
Lifecycle
请注意,常规接口是普通的
显式启动和停止通知的协定,并不意味着自动启动
在上下文刷新时。用于对自动启动的精细控制和平滑
停止特定 bean(包括 startup 和 stop 阶段),请考虑实现
扩展接口。 另外,请注意,不保证在销毁之前收到停止通知。
在常规关闭时,所有 bean 首先收到停止通知
正在传播常规销毁回调。但是,在
上下文的生命周期或停止刷新尝试时,仅调用 destroy 方法。 |
启动和关闭调用的顺序可能很重要。如果 “depends-on”
关系存在于任意两个对象之间,则依赖端在其
依赖项,并且它在依赖项之前停止。然而,有时,直接的
依赖项是未知的。您可能只知道特定类型的对象应该启动
在其他类型的对象之前。在这些情况下,接口定义
另一个选项,即在其超级接口 .下面的清单显示了接口的定义:SmartLifecycle
getPhase()
Phased
Phased
public interface Phased {
int getPhase();
}
下面的清单显示了接口的定义:SmartLifecycle
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
开始时,阶段最低的对象首先启动。停止时,
遵循 Reverse Order。因此,实现 和
其方法 returns 将是第一个开始的
也是最后一个停下来的。在频谱的另一端,相位值 表示对象应最后启动并停止
first (可能是因为它依赖于其他进程运行)。在考虑
phase 值,同样重要的是要知道,任何未实现的 “normal” 对象的默认 phase 是 。因此,任何
负相位值表示对象应在这些标准之前开始
组件(并在它们之后停止)。对于任何正相位值,情况正好相反。SmartLifecycle
getPhase()
Integer.MIN_VALUE
Integer.MAX_VALUE
Lifecycle
SmartLifecycle
0
定义的 stop 方法接受回调。任何
实现必须在该实现的
shutdown 过程完成。这将在必要时启用异步关闭,因为
接口的默认实现 , 等待对象组的超时值
在每个阶段中调用该回调。默认的每阶段超时为 30 秒。
您可以通过在上下文中定义名为 的 bean 来覆盖默认生命周期处理器实例。如果只想修改超时,
定义以下内容就足够了:SmartLifecycle
run()
LifecycleProcessor
DefaultLifecycleProcessor
lifecycleProcessor
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
如前所述,该接口为
刷新和关闭上下文。后者驱动关闭
进程,就好像已经被显式调用一样,但它发生在上下文为
关闭。另一方面,'refresh' 回调启用了 bean 的另一个功能。刷新上下文时(在所有对象都已
instantiated 和 initialized),则会调用该回调。此时,
default lifecycle processor 检查每个对象的方法返回的布尔值。如果 ,则该对象为
启动,而不是等待显式调用上下文的 OR
它自己的方法(与上下文刷新不同,上下文启动不会发生
automatically 用于标准上下文实现)。值和任何
“depends-on” 关系确定 Startup Sequence,如前所述。LifecycleProcessor
stop()
SmartLifecycle
SmartLifecycle
isAutoStartup()
true
start()
phase
在非 Web 应用程序中正常关闭 Spring IoC 容器
本节仅适用于非 Web 应用程序。Spring 基于 Web 的实现已经有代码可以正常关闭
当相关 Web 应用程序关闭时,Spring IoC 容器。 |
如果你在非 Web 应用程序环境中使用 Spring 的 IoC 容器(对于 例如,在富客户端桌面环境中),使用 JVM 的 JVM 中。这样做可以确保正常关闭,并在 singleton bean 的实例,以便释放所有资源。您仍必须配置 并正确实现这些 destroy 回调。
要注册 shutdown 钩子,请调用
在接口上声明,如下例所示:registerShutdownHook()
ConfigurableApplicationContext
-
Java
-
Kotlin
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 核心容器以线程安全的方式发布创建的单例实例, 通过单例锁保护访问并保证在其他线程中的可见性。
因此,应用程序提供的 Bean 类不必关心
其初始化状态的可见性。常规配置字段不必为
标记,只要它们仅在初始化阶段发生突变,
提供类似于 Even for setter based configuration 的可见性保证
state 的 state 在初始阶段是可变的。如果此类字段在
bean 创建阶段及其随后的初始发布,则每当访问它们时,都需要将它们声明为公共锁或由公共锁保护。volatile
final
volatile
请注意,在单例 bean 实例中对此类配置状态的并发访问,
例如,对于 Controller 实例或 Repository 实例,在
这样从容器端开始的 SAFE 初始发布。这包括在通用单例锁中处理的常见单例实例。FactoryBean
对于销毁回调,配置状态保持线程安全,但任何运行时
初始化和销毁之间累积的状态应保存在线程安全状态
结构(或在简单情况下的字段中)按照常见的 Java 准则。volatile
如上所示的深度集成涉及运行时可变状态,例如
必须声明为 .虽然常见的
生命周期回调遵循一定的顺序,例如,开始回调保证
仅在完全初始化后发生,并且仅在初始启动后停止回调,
销毁前公共停止安排有一个特殊情况:它是 strongly
建议任何此类 bean 中的内部状态也允许立即
destroy 回调,因为这可能发生在非常规
在取消引导后或由另一个 Bean 导致停止超时的情况下关闭。Lifecycle
runnable
volatile
ApplicationContextAware
和BeanNameAware
当 an 创建实现该接口的对象实例时,将提供该实例
并引用该 .下面的清单显示了定义
的界面中:ApplicationContext
org.springframework.context.ApplicationContextAware
ApplicationContext
ApplicationContextAware
public interface ApplicationContextAware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
因此,bean 可以通过编程方式操作创建它们
通过接口或将引用强制转换为已知的
子类(例如 ,它公开了
附加功能)。一种用途是以编程方式检索其他 bean。
有时此功能很有用。但是,一般来说,您应该避免使用它,因为
它将代码耦合到 Spring,并且不遵循 Inversion of Control 风格,
其中,协作者作为属性提供给 bean。的其他方法提供对文件资源的访问、发布应用程序事件、
并访问 .这些附加功能在 ApplicationContext
的附加功能中进行了描述。ApplicationContext
ApplicationContext
ConfigurableApplicationContext
ApplicationContext
MessageSource
自动装配是获取对 .传统模式和自动装配模式
(如 Autowiring Collaborators 中所述)可以为构造函数参数或 setter 方法参数提供 type 的依赖项,
分别。为了提高灵活性,包括自动装配字段和
多个参数方法,使用基于注释的自动装配功能。如果这样做,
自动连接到字段、构造函数参数或方法
参数,如果字段、构造函数或
有问题的方法带有注释。有关更多信息,请参阅使用 @Autowired
。ApplicationContext
constructor
byType
ApplicationContext
ApplicationContext
ApplicationContext
@Autowired
当 an 创建实现该接口的类时,该类会附带
对其关联对象定义中定义的名称的引用。以下清单
显示了 BeanNameAware 接口的定义:ApplicationContext
org.springframework.beans.factory.BeanNameAware
public interface BeanNameAware {
void setBeanName(String name) throws BeansException;
}
该回调在填充普通 bean 属性之后但在
初始化回调,例如 OR 自定义
init-method 的InitializingBean.afterPropertiesSet()
其他接口Aware
除了 and (前面讨论过),
Spring 提供了广泛的回调接口,让 bean 向容器指示
它们需要一定的基础设施依赖性。作为一般规则,该名称表示
dependency 类型。下表总结了最重要的接口:ApplicationContextAware
BeanNameAware
Aware
Aware
名字 | 注入的依赖项 | 解释于... |
---|---|---|
|
声明。 |
|
|
封闭 的事件发布者 . |
|
|
用于加载 bean 类的类加载器。 |
|
|
声明。 |
|
|
声明 Bean 的名称。 |
|
|
定义了 weaver,用于在加载时处理类定义。 |
|
|
用于解析消息的配置策略(支持参数化和 国际化)。 |
|
|
Spring JMX 通知发布者。 |
|
|
配置了 loader ,用于对资源的低级访问。 |
|
|
容器运行的当前情况。仅在 web-aware Spring 中有效。 |
|
|
容器运行的当前情况。仅在 web-aware Spring 中有效。 |
再次注意,使用这些接口会将您的代码绑定到 Spring API,并且不会 遵循 Inversion of Control 样式。因此,我们建议将它们用于基础设施 需要以编程方式访问容器的 bean。