Spring——定制 Bean 的性质

文章目录

  • 生命周期回调
    • 初始化回调
    • 销毁回调
    • 默认初始化和销毁​​方法
    • 结合生命周期机制
    • 启动和关闭回调
  • ApplicationContextAware 和 BeanNameAware
  • 其他Aware接口

生命周期回调

要与容器对 bean 生命周期的管理进行交互,您可以实现 Spring InitializingBean和DisposableBean接口。
容器调用 afterPropertiesSet()和destroy(),以便让 bean 在初始化和销毁​​ bean 时执行某些操作。

JSR-250@PostConstruct@PreDestroy注释通常被认为是在现代 Spring 应用程序中接收生命周期回调的最佳实践。使用这些注释意味着您的 bean 不会与 Spring 特定的接口耦合。有关详细信息,请参阅使用@PostConstruct和@PreDestroy
如果您不想使用 JSR-250 注释但仍想删除耦合,请考虑init-methodbeandestroy-method定义元数据。

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

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

本节介绍生命周期回调接口。

初始化回调

org.springframework.beans.factory.InitializingBean接口 允许容器在设置 bean 的所有必要属性后执行初始化工作
该InitializingBean接口指定一个方法:

void afterPropertiesSet() throws Exception;

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

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

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

前面的示例与下面的示例(由两个清单组成)具有几乎完全相同的效果:

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

	@Override
	public void afterPropertiesSet() {
		// do some initialization work
	}
}

然而,前面两个例子中的第一个并没有将代码与 Spring 耦合。

请注意,@PostConstruct初始化方法通常在容器的单例创建锁中执行。只有在从该 @PostConstruct方法返回后,bean 实例才被视为已完全初始化并准备发布给其他人。这些单独的初始化方法仅用于验证配置状态并可能根据给定的配置准备一些数据结构,但不会进行任何与外部 bean 访问相关的进一步活动。否则,存在初始化死锁的风险。
对于需要触发昂贵的初始化后活动的情况,例如异步数据库准备步骤,您的 bean 应该实现 SmartInitializingSingleton.afterSingletonsInstantiated()或依赖上下文刷新事件:实现ApplicationListener或声明其注释等效项@EventListener(ContextRefreshedEvent.class)。这些变体在所有常规单例初始化之后出现,因此在任何单例创建锁之外。
或者,您可以实现(Smart)Lifecycle接口并与容器的整体生命周期管理集成,包括自动启动机制、预销毁停止步骤和潜在的停止/重新启动回调(见下文)。

销毁回调

org.springframework.beans.factory.DisposableBean 实现该接口 可让 bean 在包含它的容器被销毁时获得回调
该 DisposableBean接口指定一个方法:

void destroy() throws Exception;

同样建议您不要使用DisposableBean回调接口,因为它会不必要地将代码与 Spring 耦合。
或者,我们建议使用注释@PreDestroy或指定 bean 定义支持的通用方法。
使用基于 XML 的配置元数据,您可以使用destroy-method上的属性。
使用 Java 配置,您可以使用destroyMethod的属性@Bean。请参阅 接收生命周期回调
考虑以下定义:

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

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

上述定义与以下定义几乎具有完全相同的效果:

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

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

然而,前面两个定义中的第一个并没有将代码与 Spring 耦合。

请注意,Spring 还支持推断销毁方法,检测公共close 或 shutdown方法。这是Java 配置类中@Bean方法的默认行为,并自动匹配java.lang.AutoCloseable或java.io.Closeable 实现,而不会将销毁逻辑耦合到 Spring。

默认初始化和销毁​​方法

当您编写不使用 Spring 特定InitializingBean和DisposableBean回调接口的初始化和销毁​​方法回调时,
您通常会编写具有诸如init()等名称的方法。
理想情况下,此类生命周期回调方法的名称在整个项目中是标准化的,以便所有开发人员都使用相同的方法名称initialize()和dispose()并确保一致性。

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

假设你的初始化回调方法名为init(),销毁回调方法名为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.");
		}
	}
}

然后您可以在类似下面的 bean 中使用该类:

<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 类有这样的方法,则会在适当的时候调用它。

如果现有的 Bean 类已经具有与约定不一致的回调方法,则可以通过使用其自身的init-method和destroy-method属性指定方法名称(在 XML 中)来覆盖默认方法

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

结合生命周期机制

从 Spring 2.5 开始,您有三个选项可以控制 Bean 生命周期行为:

  • InitializingBean 和 DisposableBean 回调接口
  • 自定义 init() 和 destroy() 方法
  • 注解 @PostConstruct​ 和 @PreDestroy

如果为一个 bean 配置了多个生命周期机制,并且每个机制都配置了不同的方法名称,则每个配置的方法都会按照此注释后列出的顺序运行。
但是,如果为多个生命周期机制配置了相同的方法名称(例如, init()为初始化方法配置),则该方法将运行一次,如上 一节所述。

为同一个bean配置多种生命周期机制,有不同的初始化方法,调用方式如下:

  • 注释 @PostConstruct 方法

  • 按照 InitializingBean 回调接口定义 afterPropertiesSet()

  • 自定义配置 init() 方法

销毁方法按相同顺序调用:

  • 注释方法 @PreDestroy

  • 按照 DisposableBean 回调接口定义 destroy()

  • 自定义配置 destroy() 方法

启动和关闭回调

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

public interface Lifecycle {

	void start();

	void stop();

	boolean isRunning();
}

任何 Spring 管理的对象都可以实现该Lifecycle接口。然后,当 ApplicationContext本身收到启动和停止信号时(例如,对于运行时的停止/重新启动场景),它会将这些调用级联到Lifecycle上下文中定义的所有实现。它通过委托给 LifecycleProcessor 来实现这一点,如以下清单所示:

public interface LifecycleProcessor extends Lifecycle {

	void onRefresh();

	void onClose();
}

注意,LifecycleProcessor本身是Lifecycle接口的扩展 。它还添加了另外两种方法来响应上下文的 刷新关闭

请注意,常规org.springframework.context.Lifecycle接口是显式启动和停止通知的简单契约,并不意味着在上下文刷新时自动启动。
若要对自动启动进行细粒度控制并正常停止特定 bean(包括启动和停止阶段),请考虑实现扩展接口org.springframework.context.SmartLifecycle。
另外,请注意,停止通知不一定在销毁之前发出。在常规关闭时,所有Lifecyclebean 都会先收到停止通知,然后才会传播一般销毁回调。但是,在上下文生命周期内进行热刷新或停止刷新尝试时,只会调用销毁方法。

启动和关闭调用的顺序可能很重要。如果任何两个对象之间存在“依赖”关系,则依赖方在其依赖项之后启动,并在其依赖项之前停止。但是,有时直接依赖关系是未知的。您可能只知道某种类型的对象应该先于另一种类型的对象启动。
在这些情况下,接口SmartLifecycle定义了另一个选项,即Phased在其超接口上定义的方法getPhase() 。
以下清单显示了Phased接口的定义:

public interface Phased {

	int getPhase();
}

以下清单显示了SmartLifecycle接口的定义:

public interface SmartLifecycle extends Lifecycle, Phased {

	boolean isAutoStartup();

	void stop(Runnable callback);
}

启动时,具有最低阶段的对象首先启动。停止时,遵循相反的顺序。
因此,实现SmartLifecycle并且其getPhase()方法返回的对象Integer.MIN_VALUE将是最先启动和最后停止的对象之一。
在另一端,阶段值 Integer.MAX_VALUE表示对象应最后启动并最先停止(可能是因为它依赖于其他正在运行的进程)。

SmartLifecycle 定义的 stop 方法接受回调。任何实现都必须run()在该实现的关闭过程完成后调用该回调的方法。这样可以在必要时启用异步关闭,因为接口的默认实现LifecycleProcessor会 DefaultLifecycleProcessor等待每个阶段内的对象组达到其超时值后才调用该回调。每个阶段的默认超时为 30 秒。您可以通过定义在上下文中命名的 bean 来覆盖默认的生命周期处理器实例 lifecycleProcessor。如果您只想修改超时,定义以下内容就足够了:

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

如前所述,LifecycleProcessor接口还定义了用于刷新和关闭上下文的回调方法。后者驱动关闭过程,就像stop()被明确调用一样,但它发生在上下文关闭时。另一方面,“刷新”回调启用了 SmartLifecyclebean 的另一个功能。当刷新上下文时(在所有对象都已实例化和初始化之后),将调用该回调。此时,默认生命周期处理器会检查每个 SmartLifecycle对象的isAutoStartup()方法返回的布尔值。如果是true,则该对象此时启动,而不是等待显式调用上下文或其自己的start()方法(与上下文刷新不同,对于标准上下文实现,上下文启动不会自动发生)。值phase和任何“依赖”关系决定了启动顺序,如前所述。

ApplicationContextAware 和 BeanNameAware

当ApplicationContext 创建的 对象实例 实现接口 org.springframework.context.ApplicationContextAware时 ,该实例将获得ApplicationContext的引用。

public interface ApplicationContextAware {

	void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

一种用途是编程式检索其他 bean。有时此功能很有用。但是,通常情况下,您应该避免使用它,因为它将代码与 Spring 耦合,并且不遵循控制反转样式,其中协作者作为属性提供给 bean。ApplicationContext的其他方法提供 对文件资源的访问、发布应用程序事件和访问。

当ApplicationContext 创建实现 org.springframework.beans.factory.BeanNameAware 接口的类 时,该类将获得对其关联对象定义中定义的名称的引用。以下清单显示了 BeanNameAware 接口的定义:

public interface BeanNameAware {

	void setBeanName(String name) throws BeansException;
}

其他Aware接口

除了ApplicationContextAware和BeanNameAware(前面讨论过)之外,Spring 还提供了广泛的Aware回调接口,让 bean 向容器指示它们需要特定的基础架构依赖项。一般来说,名称表示依赖项类型。下表总结了最重要的Aware接口:

接口 注入依赖 解释如下…
ApplicationContextAware 宣告ApplicationContext ApplicationContextAware和BeanNameAware
ApplicationEventPublisherAware 附件的事件发布者ApplicationContext 附加功能ApplicationContext
BeanClassLoaderAware 用来加载 bean 类的类加载器。 实例化 Bean
BeanFactoryAware 宣告BeanFactory APIBeanFactory
BeanNameAware 声明 bean 的名称。 ApplicationContextAware和BeanNameAware
LoadTimeWeaverAware 定义编织器用于在加载时处理类定义。 在 Spring 框架中使用 AspectJ 进行加载时编织
MessageSourceAware 配置消息解析策略(支持参数化和国际化)。 附加功能ApplicationContext
NotificationPublisherAware Spring JMX 通知发布者。 通知
ResourceLoaderAware 配置加载器以进行低级资源访问。 资源
ServletConfigAware ServletConfig容器 当前运行。仅在支持 Web 的 Spring 中有效ApplicationContext Spring MVC
ServletContextAware ServletContext容器 当前运行。仅在支持 Web 的 Spring 中有效ApplicationContext Spring MVC

再次注意,使用这些接口会将您的代码绑定到 Spring API,并且不遵循控制反转样式。因此,我们建议将它们用于需要以编程方式访问容器的基础架构 bean。

上一篇:python pytorch之torch.flip 按轴翻转/倒叙排列 方法


下一篇:Web前端框架对比2016:探索主流技术的优劣势