Spring Framework 提供了许多可用于自定义性质的接口 豆子。本部分按如下方式对它们进行分组:
生命周期回调
要与容器对 Bean 生命周期的管理进行交互,您可以实现
弹簧和接口。容器要求前者,后者让豆子
在初始化和销毁 Bean 时执行某些操作。InitializingBeanDisposableBeanafterPropertiesSet()destroy()
|
JSR-250 和注释通常被认为是最好的
在现代 Spring 应用程序中接收生命周期回调的练习。使用这些
注解意味着您的 Bean 没有耦合到 Spring 特定的接口。
有关详细信息,请参阅使用 如果您不想使用 JSR-250 注释,但仍想删除
耦合、考虑和 Bean 定义元数据。 |
在内部,Spring Framework 使用实现来处理任何
回调接口,它可以查找并调用相应的方法。如果您需要自定义
功能或其他生命周期行为 Spring 默认不提供,您可以
实现自己。有关详细信息,请参阅容器扩展点。BeanPostProcessorBeanPostProcessor
除了初始化和销毁回调之外,Spring 管理的对象还可以
还要实现接口,以便这些对象可以参与
启动和关闭过程,由容器自身的生命周期驱动。Lifecycle
本节将对生命周期回调接口进行介绍。
初始化回调
该接口允许 bean
在容器设置了所有必要的属性后执行初始化工作
豆。该接口指定了一个方法:org.springframework.beans.factory.InitializingBeanInitializingBean
void afterPropertiesSet() throws Exception;
我们建议您不要使用该接口,因为它
不必要地将代码耦合到 Spring。或者,我们建议使用
@PostConstruct注释或
指定 POJO 初始化方法。对于基于 XML 的配置元数据,
可以使用该属性指定具有 void 的方法的名称
无参数签名。使用 Java 配置时,可以使用 的属性。请参阅接收生命周期回调。请看以下示例:InitializingBeaninit-methodinitMethod@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。
|
请注意,初始化方法通常被执行
在容器的单例创建锁中。仅考虑 Bean 实例
完全初始化,并准备在从方法返回后发布给其他人。这种单独的初始化方法仅意味着
用于验证配置状态并可能准备一些数据结构
基于给定的配置,但没有进一步的外部 Bean 访问活动。
否则,存在初始化死锁的风险。 对于要触发代价高昂的初始化后活动的场景,
例如,异步数据库准备步骤,你的 Bean 应该实现或依赖上下文
刷新事件:实现或
声明其注释等价。
这些变体在所有常规单例初始化之后出现,因此
在任何单例创建锁之外。 或者,您可以实现该接口并集成
容器的整体生命周期管理,包括自动启动机制,
销毁前的停止步骤,以及潜在的停止/重新启动回调(见下文)。 |
销毁回调
实现该接口可以
Bean 在包含它的容器被销毁时获取回调。该接口指定了一个方法:org.springframework.beans.factory.DisposableBeanDisposableBean
void destroy() throws Exception;
我们建议您不要使用回调接口,因为它
不必要地将代码耦合到 Spring。或者,我们建议使用
@PreDestroy注释或
指定 Bean 定义支持的泛型方法。使用基于 XML
配置元数据,则可以在 .
使用 Java 配置时,可以使用 的属性。请参阅接收生命周期回调。请考虑以下定义:DisposableBeandestroy-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 还支持对销毁方法的推理,检测公共或方法。这是 Java 配置中方法的缺省行为
类并自动匹配或实现,也不将销毁逻辑耦合到 Spring。closeshutdown@Beanjava.lang.AutoCloseablejava.io.Closeable
对于使用 XML 的销毁方法推理,您可以分配属性
元素的特殊值,它指示 Spring 自动
检测特定 Bean 定义的 Bean 类上的公共或方法。
您还可以在属性上设置此特殊值
元素以将此行为应用于一整套 Bean 定义(请参阅缺省初始化和销毁方法)。destroy-method<bean>(inferred)closeshutdown(inferred)default-destroy-method<beans> |
|
对于延长的关断阶段,您可以实现接口并接收
调用任何单例 Bean 的销毁方法之前的早期停止信号。
您还可以实现一个有时间限制的停止步骤,其中容器
将等待所有此类停止处理完成,然后再继续销毁方法。 |
默认初始化和销毁方法
当您编写不使用
特定于 Spring 的接口和回调接口,您
通常编写名称为 、 、 、
等等。理想情况下,此类生命周期回调方法的名称是标准化的
一个项目,以便所有开发人员使用相同的方法名称并确保一致性。InitializingBeanDisposableBeaninit()initialize()dispose()
您可以将 Spring 容器配置为“查找”命名初始化和销毁
每个 Bean 上的回调方法名称。这意味着,作为应用程序开发人员,您
可以编写应用程序类并使用名为 的初始化回调 ,
无需为每个 Bean 定义配置属性。
Spring IoC 容器在创建 Bean 时调用该方法(并且根据
使用前面描述的标准生命周期回调协定)。
此功能还强制执行一致的初始化命名约定和
销毁方法回调。init()init-method="init"
假设您的初始化回调方法已命名,并且您的销毁
回调方法被命名为 。然后,您的类类似于
以下示例: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 上调用的方法
类作为初始化方法回调。创建和组装 Bean 时,如果
Bean 类有这样的方法,它会在适当的时间调用。default-init-method<beans/>init
您可以通过使用 top 级元素上的属性来类似地(即在 XML 中)配置 destroy 方法回调。default-destroy-method<beans/>
现有 Bean 类已具有以差异命名的回调方法
使用约定,您可以通过指定(在 XML 中)来覆盖默认值
方法名称,通过使用本身的 AND 属性。init-methoddestroy-method<bean/>
Spring 容器保证调用配置的初始化回调
在为 Bean 提供所有依赖项后立即。因此,初始化
在原始 Bean 引用上调用回调,这意味着 AOP 拦截器等
第四个尚未应用于豆子。首先完全创建一个目标 Bean,然后
然后应用具有拦截器链的 AOP 代理(例如)。如果目标
Bean 和代理是分开定义的,您的代码甚至可以与 RAW 交互
目标 Bean,绕过代理。因此,适用
拦截器与该方法,因为这样做会耦合
将 Bean 定位到其代理或拦截器,并在代码时留下奇怪的语义
直接与原始目标 Bean 交互。init
结合生命周期机制
从 Spring 2.5 开始,您有三个选项来控制 Bean 生命周期行为:
-
InitializingBean和DisposableBean回调接口 -
自定义和方法
init()destroy() -
-
您可以组合这些机制来控制给定的 Bean。
-
如果为一个 Bean 配置了多个生命周期机制,并且每个机制都
配置了不同的方法名称,然后每个配置的方法都在
在此注释之后列出的顺序。但是,如果为这些生命周期机制中的多个配置了相同的方法名称(例如,对于初始化方法),
该方法运行一次,如上一节所述。init() |
为同一 Bean 配置了多个生命周期机制,具有不同的 初始化方法,调用如下:
-
注释的方法
@PostConstruct -
afterPropertiesSet()由回调接口定义InitializingBean -
自定义配置方法
init()
销毁方法的调用顺序相同:
-
注释的方法
@PreDestroy -
destroy()由回调接口定义DisposableBean -
自定义配置方法
destroy()
启动和关闭回调
该接口为任何具有自己的对象定义基本方法
生命周期要求(例如启动和停止某些后台进程):Lifecycle
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
任何 Spring 管理的对象都可以实现该接口。然后,当自身接收到启动和停止信号时(例如,用于停止/重新启动
场景),它将这些调用级联到所有实现
在该上下文中定义。它通过委托给 来实现此目的,如图所示
在以下列表中:LifecycleApplicationContextLifecycleLifecycleProcessor
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onClose();
}
请注意,它本身是接口的扩展。它还添加了另外两种方法,用于对正在刷新的上下文做出反应
并关闭。LifecycleProcessorLifecycle
|
请注意,常规接口是普通的
显式启动和停止通知的约定,并不意味着自动启动
在上下文刷新时。用于对自动启动的细粒度控制和优雅
停止特定 Bean(包括启动和停止阶段),请考虑实现
扩展接口。 另外,请注意,不保证在销毁之前会发出停止通知。
在常规关机时,所有 Bean 首先收到停止通知,然后
一般销毁回调正在传播中。但是,在热刷新期间
上下文的生存期或停止刷新尝试时,仅调用销毁方法。 |
启动和关闭调用的顺序可能很重要。如果“依赖”
关系存在于任意两个对象之间,从属方在其
依赖性,并且它在其依赖性之前停止。然而,有时,直接的
依赖关系未知。您可能只知道某种类型的对象应该启动
在另一种类型的对象之前。在这些情况下,接口定义
另一种选择,即在其超级接口上定义的方法。以下列表显示了接口的定义:SmartLifecyclegetPhase()PhasedPhased
public interface Phased {
int getPhase();
}
以下列表显示了接口的定义:SmartLifecycle
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
启动时,相位最低的物体首先启动。停止时,
遵循相反的顺序。因此,实现和
其方法返回将是第一个开始的方法之一
最后一个停下来。在光谱的另一端,相位值 表示对象应最后启动和停止
首先(可能是因为它依赖于要运行的其他进程)。在考虑
相值,同样重要的是要知道任何未实现的“正常”对象的默认相位是 。因此,任何
负相位值表示对象应在这些标准之前开始
组件(并在它们之后停止)。对于任何正相位值,情况正好相反。SmartLifecyclegetPhase()Integer.MIN_VALUEInteger.MAX_VALUELifecycleSmartLifecycle0
定义的 stop 方法接受回调。任何
实现必须在该实现的
关机过程完成。这样就可以在必要时进行异步关闭,因为
接口的默认实现 ,等待对象组的超时值
在每个阶段调用该回调。默认的每相超时为 30 秒。
您可以通过定义上下文中命名的 Bean 来覆盖缺省生命周期处理器实例。如果只想修改超时,
定义以下内容就足够了:SmartLifecyclerun()LifecycleProcessorDefaultLifecycleProcessorlifecycleProcessor
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
如前所述,该接口定义了
刷新和关闭上下文。后者驱动关机
过程,就好像被显式调用了一样,但它发生在上下文
关闭。另一方面,“refresh”回调启用了 Bean 的另一个功能。刷新上下文时(在所有对象都刷新之后)
实例化和初始化),则调用该回调。在这一点上,
默认生命周期处理器检查每个对象的方法返回的布尔值。如果 ,则该对象是
从那时开始,而不是等待显式调用上下文的 or
它自己的方法(与上下文刷新不同,上下文启动不会发生
自动用于标准上下文实现)。值和任何
如前所述,“依赖”关系决定了启动顺序。LifecycleProcessorstop()SmartLifecycleSmartLifecycleisAutoStartup()truestart()phase
在非 Web 应用程序中正常关闭 Spring IoC 容器
|
本节仅适用于非 Web 应用程序。Spring 的基于 Web 的实现已经有了可以正常关闭的代码
关闭相关 Web 应用程序时的 Spring IoC 容器。 |
如果您在非 Web 应用程序环境中使用 Spring 的 IoC 容器(对于 例如,在富客户端桌面环境中),使用 JVM。这样做可以确保正常关闭,并在您的 单例 Bean,以便释放所有资源。您仍必须配置 并正确实现这些销毁回调。
若要注册关闭挂钩,请调用以下方法
在接口上声明,如以下示例所示: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 类不必关注
其初始化状态的可见性。常规配置字段不必是
只要它们仅在初始化阶段发生突变,就被标记为,
提供类似于基于设置器的配置的可见性保证
在初始阶段可变的状态。如果这些字段在
Bean 创建阶段及其随后的初始发布阶段,无论何时访问它们,都需要声明为公共锁或由公共锁保护。volatilefinalvolatile
请注意,在单例 Bean 实例中并发访问此类配置状态时,
例如,对于控制器实例或存储库实例,在之后是完全线程安全的
从容器端进行这种安全的初始发布。这包括在常规单例锁中处理的常见单例实例。FactoryBean
对于销毁回调,配置状态保持线程安全,但任何运行时
在初始化和销毁之间累积的状态应保持在线程安全状态
结构(或简单情况下的字段)按照通用 Java 指南。volatile
如上所示,更深层次的集成涉及运行时可变状态,例如
必须声明为 的字段。虽然常见的
生命周期回调遵循一定的顺序,例如,开始回调保证
仅在完全初始化后发生,并且仅在初始启动后才发生停止回调,
销毁前的共同停止安排有一个特殊情况:它强烈
建议任何此类 Bean 中的内部状态也允许立即
在没有先前停止的情况下销毁回调,因为这可能发生在非常期间
在取消引导程序后关闭,或者在另一个 Bean 导致停止超时的情况下关闭。Lifecyclerunnablevolatile
ApplicationContextAware和BeanNameAware
当创建实现接口的对象实例时,将提供该实例
参考那个.以下列表显示了定义
的接口:ApplicationContextorg.springframework.context.ApplicationContextAwareApplicationContextApplicationContextAware
public interface ApplicationContextAware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
因此,bean 可以编程地操作创建它们的东西,
通过接口或通过将引用转换为已知的
此接口的子类(例如 ,它公开了
附加功能)。一种用途是以编程方式检索其他 Bean。
有时,此功能很有用。但是,一般来说,您应该避免它,因为
它将代码耦合到 Spring 并且不遵循 Inversion of Control 样式,
其中协作者作为属性提供给 Bean。提供对文件资源、发布应用程序事件、
并访问 .ApplicationContext 的其他功能中介绍了这些附加功能。ApplicationContextApplicationContextConfigurableApplicationContextApplicationContextMessageSource
自动连线是获取对 的引用的另一种替代方法。传统和自动接线模式
(如 Autowiring Collaborators 中所述)可以为构造函数参数或 setter 方法参数提供类型的依赖关系,
分别。获得更大的灵活性,包括自动布线字段和
多种参数方法,使用基于注释的自动布线功能。如果你这样做,
自动连接到字段、构造函数参数或方法中
如果字段、构造函数或
有问题的方法带有注释。有关详细信息,请参阅使用 @Autowired。ApplicationContextconstructorbyTypeApplicationContextApplicationContextApplicationContext@Autowired
当创建实现接口的类时,将为该类提供
对其关联对象定义中定义的名称的引用。以下列表
显示 BeanNameAware 接口的定义:ApplicationContextorg.springframework.beans.factory.BeanNameAware
public interface BeanNameAware {
void setBeanName(String name) throws BeansException;
}
回调是在普通 Bean 属性的填充之后调用的,但在
初始化回调,例如 或自定义
init-方法。InitializingBean.afterPropertiesSet()
其他接口Aware
除了和(前面讨论过),
Spring 提供了广泛的回调接口,让 Bean 向容器发出指示
它们需要一定的基础设施依赖性。作为一般规则,该名称表示
依赖项类型。下表总结了最重要的接口:ApplicationContextAwareBeanNameAwareAwareAware
| 名字 | 注入的依赖关系 | 解释... |
|---|---|---|
|
声明。 |
|
|
封闭的事件发布者 。 |
|
|
类装入器用于装入 Bean 类。 |
|
|
声明。 |
|
|
声明 Bean 的名称。 |
|
|
定义的 weaver,用于在加载时处理类定义。 |
|
|
配置的解析消息的策略(支持参数化和 国际化)。 |
|
|
Spring JMX 通知发布者。 |
|
|
配置了用于对资源进行低级别访问的加载程序。 |
|
|
容器运行的电流。仅在具有 Web 感知的 Spring 中有效。 |
|
|
容器运行的电流。仅在具有 Web 感知的 Spring 中有效。 |
再次注意,使用这些接口会将您的代码绑定到 Spring API,而不是 遵循“控制反转”样式。因此,我们推荐它们用于基础设施 需要以编程方式访问容器的 Bean。