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 {@Overridepublic 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 {@Overridepublic 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 methodpublic 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宣告ApplicationContextApplicationContextAwareBeanNameAware
ApplicationEventPublisherAware附件的事件发布者ApplicationContext附加功能ApplicationContext
BeanClassLoaderAware用来加载 bean 类的类加载器。实例化 Bean
BeanFactoryAware宣告BeanFactoryAPIBeanFactory
BeanNameAware声明 bean 的名称。ApplicationContextAwareBeanNameAware
LoadTimeWeaverAware定义编织器用于在加载时处理类定义。在 Spring 框架中使用 AspectJ 进行加载时编织
MessageSourceAware配置消息解析策略(支持参数化和国际化)。附加功能ApplicationContext
NotificationPublisherAwareSpring JMX 通知发布者。通知
ResourceLoaderAware配置加载器以进行低级资源访问。资源
ServletConfigAwareServletConfig容器 当前运行。仅在支持 Web 的 Spring 中有效ApplicationContextSpring MVC
ServletContextAwareServletContext容器 当前运行。仅在支持 Web 的 Spring 中有效ApplicationContextSpring MVC

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/19434.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mariadb操作命令指南

MariaDB简介 ​ 以下内容仅是站长或网友个人学习笔记、总结和研究收藏。不保证正确性&#xff0c;因使用而带来的风险与本站无关&#xff01; 数据库应用程序与主应用程序分开存在&#xff0c;并存储数据集合。 每个数据库都使用一个或多个API来创建&#xff0c;访问&#xf…

分立元件实现稳压

电路原理图 优点&#xff1a;电压精度高&#xff0c;可以调整输出电压 缺点&#xff1a;压差大时效率较低&#xff0c;发热严重。 参考连接 TL431-高效5V精密稳压器-电路知识干货 (qq.com)https://mp.weixin.qq.com/s?__bizMzkxNzIxNTc5OQ&mid2247484878&idx1&…

操作系统教材第6版——个人笔记1

第一章 计算机操作系统概述 操作系统是计算机系统中最重要的系统软件&#xff0c;它统一管理计算机系统的硬件资源与信息资源&#xff0c;控制与调度上层软件的执行并为其提供易于使用的接口。从资源管理、程序控制、操作控制、人机交互、程序接口、系统结构6个角度深入观察操…

Github 如何配置 PNPM 的 CI 环境

最近出于兴趣在写一个前端框架 echox&#xff0c;然后在 Github 上给它配置了最简单的 CI 环境&#xff0c;这里简单记录一下。 特殊目录 首先需要在项目根目录里面创建 Github 仓库中的一个特殊目录&#xff1a;.github/workflows&#xff0c;用于存放 Github Actions 的工作…

【linux kernel】一文浅析linux HID核心

文章目录 一、hid核心初始化二、hid总线probe过程分析三、hid总线match过程分析四、hid总线的uevent过程五、usbhid驱动分析(1)struct hid_driver(2)usbhid的探测行为(3)usb_hid_driver实现六、总结一、hid核心初始化 在linux内核中,HID核心是完成HID功能的关键组件,如果内核…

Kubernetes中应用如何通过Service访问的原理

在Kubernetes中&#xff0c;我们创建的应用是跑在pod里&#xff0c;应用和应用之间需要通信访问&#xff0c;可以通过Service来给pod创建一个可以供访问的负载均衡&#xff08;一个虚拟IP&#xff09;地址&#xff0c;那么别的Pod来访问这个Service背后的原理是什么呢&#xff…

269 基于matlab的四连杆机构动力学参数计算

基于matlab的四连杆机构动力学参数计算。将抽油机简化为4连杆机构&#xff0c;仿真出悬点的位移、速度、加速度、扭矩因数、游梁转角等参数&#xff0c;并绘出图形。程序已调通&#xff0c;可直接运行。 269机构动力学参数计算 位移、速度、加速度 - 小红书 (xiaohongshu.com)

段码屏|液晶显示模块|超低功耗LCD驱动芯片

1 简介 PC164S32 是一款支持 128 点 (32 4)显示 的多功能 LCD 控制器芯片&#xff0c;内部存储器RAM数据直接映射到 LCD 显示。可软件配置特性使其适用于包括 LCD 模块和显示子系统在内的多种 LCD 应用。主控制器与 PC164S32接口仅需3 或 4 条线。内置的省电模式极大的降低了功…

我给线程池管理框架hippo4j找bug

1 虚拟机参数不生效 hippo4j的docker启动脚本位于 docker/docker-startup.sh 。从下图可以看到 JAVA_OPT放在了jar包名 hippo4j-server.jar之后&#xff0c;而只有项目参数才放在jar包名之后。 实际上这里JAVA_OPT中包含虚拟机参数&#xff0c;而虚拟机参数要放在jar包名之前…

使用 CNN 训练自己的数据集

CNN&#xff08;练习数据集&#xff09; 1.导包&#xff1a;2.导入数据集&#xff1a;3. 使用image_dataset_from_directory()将数据加载tf.data.Dataset中&#xff1a;4. 查看数据集中的一部分图像&#xff0c;以及它们对应的标签&#xff1a;5.迭代数据集 train_ds&#xff0…

Shell 编程之免交互

一&#xff1a;Here Document 免交互 1.1&#xff1a;概述 Here Document 是一个特殊用途的代码块。它在 Linux Shell 中使用 I/O 重定向的方式将命令列表提供给交互式程序或命令&#xff0c;比如 ftp 、 cat 或 read 命令。 Here Document 是标准输入的一种替代品&a…

MySQL学习——在批处理模式下使用mysql

除了交互式地使用mysql来输入语句并查看结果。也可以以批处理模式运行mysql。为此&#xff0c;将你想要运行的语句放入一个文件中&#xff0c;然后告诉mysql从该文件读取输入&#xff1a; $> mysql < batch-file 如果你在Windows下运行mysql&#xff0c;并且文件中包含…

【前端每日基础】day31——uni-app

uni-app 开发详细介绍 基本概念 uni-app&#xff1a;uni-app 是一个使用 Vue.js 开发多端应用的框架&#xff0c;可以编译到微信小程序、支付宝小程序、百度小程序、字节跳动小程序、H5、App等多个平台。 跨平台&#xff1a;一次开发&#xff0c;多端部署。通过条件编译实现多…

【漏洞复现】DT-高清车牌识别摄像机 任意文件读取漏洞

0x01 产品简介 DT-高清 车牌识别摄像机是一款先进的安防设备&#xff0c;采用高清图像传感器和先进的识别算法&#xff0c;能够精准、快速地识别车牌信息。其高清晰该摄像机结合了智能识别技术&#xff0c;支持实时监宴图像质量确保在各种光照和天气条件下都能准确捕捉车牌信息…

【面试八股总结】MySQL事务:事务特性、事务并行、事务的隔离级别

参考资料&#xff1a;小林coding 一、事务的特性ACID 原子性&#xff08;Atomicity&#xff09; 一个事务是一个不可分割的工作单位&#xff0c;事务中的所有操作&#xff0c;要么全部完成&#xff0c;要么全部不完成&#xff0c;不会结束在中间某个环节。原子性是通过 undo …

CSS-in-JS学习

CSS-in-JS CSS-in-JS 是一种将样式直接写入JavaScript代码中的方法,它通常与React、Vue等现代前端框架结合使用。 1. 什么是CSS-in-JS? CSS-in-JS 是一种编写样式的方法,它允许开发者在JavaScript组件内部定义样式,通常使用类似于CSS的语法。这种方式提高了代码的可复用…

C#根据数据量自动排版标签的样例

这是一个C#根据数据量自动排版标签的样例 using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Drawing; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using HslCommuni…

go mongo 唯一索引创建

1. 登录mongo&#xff0c;创建数据库 mongosh -u $username -p $password use test 2. 查看集合索引 db.$collection_name.getIndexes() 为不存在的集合创建字段唯一索引 package mainimport ("context""fmt""log""time""go…

代码随想录算法训练营第四十五天 | 1049. 最后一块石头的重量 II、494. 目标和、474.一和零

1049. 最后一块石头的重量 II 视频讲解&#xff1a; 动态规划之背包问题&#xff0c;这个背包最多能装多少&#xff1f;LeetCode&#xff1a;1049.最后一块石头的重量II_哔哩哔哩_bilibili 代码随想录 解题思路 直接将这一些石头&#xff0c;分为两堆&#xff0c;让他们尽可能…

假如Redis⾥面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?

使⽤用 keys 指令可以扫出指定模式的 key 列列表。但是要注意 keys 指令会导致线程阻塞⼀一段时间&#xff0c;线上服务会停 顿&#xff0c;直到指令执⾏行行完毕&#xff0c;服务才能恢复。这个时候可以使⽤用 scan 指令&#xff0c; scan 指令可以⽆无阻塞的提取出指定模式 的…