Spring官方文档解读(五)之自定义 bean 的性质
Spring 框架提供了许多接口,可用于自定义 Bean 的性质。本节将它们分组如下:
- Lifecycle Callbacks
- ApplicationContextAware 和 BeanNameAware
- 其他感知接口
生命周期回调
要通过容器对 bean的生命周期进行管理,可以实现Spring中的InitializingBean
和DisposableBean
接口。容器对前者调用afterPropertiesSet()
,对后者调用destroy()
,以使 Bean 在初始化和销毁 Bean 时执行某些操作。
但在如今的Spring 应用程序中,我们更多使用的是 @PostConstruct
和@PreDestroy
注解来接收生命周期回调函数。使用这些注解意味着对应的bean 没有耦合到Spring 的接口中(比实现Spring的接口好很多)。
如果不想使用注解,但仍然想删除耦合,可以使用init-method
和destroy-method
对象定义元数据。
在内部,Spring 框架使用BeanPostProcessor
实现来处理它可以找到的任何回调接口并调用适当的方法。如果需要自定义功能或其他生命周期行为,Spring 默认不提供,则我们可以自己实现BeanPostProcessor
。
除了初始化回调和销毁回调之外,Spring 托管的对象还可以实现Lifecycle
接口,以便这些对象可以在容器自身生命周期的驱动下参与启动和关闭过程。
Initialization Callbacks
org.springframework.beans.factory.InitializingBean
接口允许容器在设置了所有必需的属性后,bean 可以执行初始化工作。 InitializingBean
接口指定一个方法:
void afterPropertiesSet() throws Exception;
建议不要使用InitializingBean
接口,因为它不必要地将代码耦合到 Spring,而建议使用@PostConstruct注解或指定 POJO 初始化方法。对于基于 XML 的配置元数据,可以使用init-method
属性指定具有无参数签名的方法名。通过 Java 配置,可以使用@Bean
的initMethod
属性。考虑以下示例:
<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 {public void afterPropertiesSet() {// do some initialization work}
}
但是,前面两个示例中的第一个示例并未将代码耦合到 Spring。
Destruction Callbacks
实现org.springframework.beans.factory.DisposableBean
接口后,当包含 bean 的容器被销毁时,bean 可以获取回调。 DisposableBean
接口指定一个方法:
void destroy() throws Exception;
建议不要使用DisposableBean
回调接口,因为它不必要地将代码耦合到 Spring。另外,建议使用@PreDestroy注解或指定 bean 定义支持的通用方法。使用基于 XML 的配置元数据时,可以在<bean/>
上使用destroy-method
属性。通过 Java 配置,可以使用@Bean
的destroyMethod
属性。考虑以下定义:
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {public void cleanup() {// do some destruction work (like releasing pooled connections)}
}
前面的定义与下面的定义几乎具有完全相同的效果:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {public void destroy() {// do some destruction work (like releasing pooled connections)}
}
但是,前面两个定义中的第一个没有将代码耦合到 Spring。
可以为<bean>
元素的destroy-method
属性分配特殊的(inferred)
值,该值指示 Spring 自动检测特定 bean 类上的公共close
或shutdown
方法。 (因此,实现java.lang.AutoCloseable
或java.io.Closeable
的任何类都将匹配.)还可以在<beans>
元素的default-destroy-method
属性上设置此特殊的(inferred)
值,以将此行为应用于整个 bean 组。这是 Java 配置的默认行为。
默认初始化和销毁方法
当编写不使用特定于 Spring 的InitializingBean
和DisposableBean
回调接口的初始化和销毁方法回调时,通常会编写诸如init()
,initialize()
,dispose()
等名称的方法。理想情况下,此类生命周期回调方法的名称应在整个项目中标准化,以便所有开发人员都使用相同的方法名称并确保一致性。
可以将 Spring 容器配置为“查找”命名的初始化,并销毁每个 bean 上的回调方法名称。这意味着,作为应用程序开发人员,可以编写应用程序类并使用名为init()
的初始化回调,而不必为每个 bean 定义配置init-method="init"
属性。 Spring IoC 容器在创建 bean 时调用该方法。此功能还对初始化和销毁方法回调强制执行一致的命名约定。
假设初始化回调方法命名为init()
,而 destroy 回调方法命名为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 类具有此类方法,则会在适当的时间调用它。
可以通过使用顶级<beans/>
元素上的default-destroy-method
属性类似地(在 XML 中)配置 destroy 方法回调。
如果现有的 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。
如果为一个 bean 配置了多个生命周期机制,并且为每个机制配置了不同的方法名称,则将按照此注解后列出的顺序执行每个已配置的方法。但是,如果为多个生命周期机制中的多个生命周期配置了相同的方法名称(例如,对于初始化方法使用init()
),则该方法将执行一次
为同一个 bean 配置的具有不同初始化方法的多种生命周期机制如下:
- 用
@PostConstruct
注解 的方法 InitializingBean
回调接口定义的afterPropertiesSet()
- 自定义配置的
init()
方法
销毁方法的调用相同:
- 用
@PreDestroy
注解 的方法 DisposableBean
回调接口定义的destroy()
- 自定义配置的
destroy()
方法
启动和关闭回调
Lifecycle
接口为具有自己的生命周期要求(例如启动和停止某些后台进程)的任何对象定义基本方法:
public interface Lifecycle {void start();void stop();boolean isRunning();
}
任何Spring管理的对象都可以实现Lifecycle
接口。然后当ApplicationContext
本身接收到启动和停止 signal 时(例如,对于运行时的停止/重新启动场景),它将把这些调用级联到在该上下文中定义的所有Lifecycle
实现。它通过委托LifecycleProcessor
来完成此任务,如以下清单所示:
public interface LifecycleProcessor extends Lifecycle {void onRefresh();void onClose();
}
请注意,LifecycleProcessor
本身是Lifecycle
接口的扩展。它还添加了两种其他方法来响应正在刷新和关闭的上下文。
请注意,常规org.springframework.context.Lifecycle
接口是用于显式启动和停止通知的普通协议,并不意味着在上下文刷新时自动启动。为了对特定 bean 的自动启动进行精细控制(包括启动阶段),要考虑实现org.springframework.context.SmartLifecycle
。
另外请注意,不能保证会在销毁之前发出停止通知。在常规关闭时,在传播常规销毁回调之前,所有Lifecycle
bean 都首先收到停止通知。但是,在上下文生存期内的热刷新或中止的刷新尝试中,仅调用 destroy 方法。
Spring中的Aware接口
这里我们又学习了Spring中的一种接口——Aware接口,这个接口中没有任何方法声明,是一个空接口,所为的只是一个标记作用(表示是此Aware接口的实现类)。所有实现此接口的类都会以Aware结尾。那么Aware接口的含义是什么呢?
其实总结起来就是Spring提供给我们的可以在Bean的内部获取Spring提供的资源的接口。
下面介绍几个Spring所提供的Aware接口:
Tables | Are |
---|---|
ApplicationContextAware | 获得当前的application context从而调用容器的服务 |
BeanNameAware | 获得到容器中Bean的名称 |
BeanFactoryAware | 获得当前bean Factory,从而调用容器的服务 |
MessageSourceAware | 得到message source从而得到文本信息 |
ApplicationEventPublisherAware | 应用时间发布器,用于发布事件 |
ResourceLoaderAware | 获取资源加载器,可以获得外部资源文件 |
Spring Aware的目的是为了让Bean获得Spring容器的服务。因为ApplicationContext接口集成了MessageSource接口、ApplicationEventPublisher接口和ResourceLoader接口,因此当Bean继承自ApplicationContextAware的时候就可以得到Spring容器的所有服务。
ApplicationContextAware 和 BeanNameAware
当ApplicationContext
创建实现了org.springframework.context.ApplicationContextAware
接口的对象实例时,该实例将获得对该ApplicationContext
的引用。以下清单显示了ApplicationContextAware
接口的定义:
public interface ApplicationContextAware {void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
因此,bean 可以通过ApplicationContext
接口或通过将引用转换为该接口的已知子类(例如ConfigurableApplicationContext
,以公开其他功能)来以编程方式操纵创建它们的ApplicationContext
。一种用途是通过编程方式检索其他 bean。有时,此功能很有用。但是,通常应避免使用它,因为它将代码耦合到 Spring,并且不遵循控制反转样式,在该样式中,将协作者作为属性提供给 bean。 ApplicationContext
的其他方法提供对文件资源的访问,发布应用程序事件以及对MessageSource
的访问。
从 Spring 2.5 开始,自动注入是获得对ApplicationContext
的引用的另一种选择。 “传统” constructor
和byType
自动装配模式可以分别为构造函数参数或 setter 方法参数提供ApplicationContext
类型的依赖。(这里建议使用基于注解的自动注入,功能更全)。如果这样做,ApplicationContext
将自动连接到期望使用ApplicationContext
类型的字段,构造函数自变量或方法参数中(如果有问题的字段,构造函数或方法带有@Autowired
注解)。
当ApplicationContext
创建实现org.springframework.beans.factory.BeanNameAware
接口的类时,该类将获得对其关联的对象定义中定义的名称的引用。以下清单显示了 BeanNameAware 接口的定义:
public interface BeanNameAware {void setBeanName(String name) throws BeansException;
}
在填充常规 bean 属性之后但在初始化回调(例如InitializingBean
,afterPropertiesSet
或自定义 init-method)之前调用该回调。
其他Aware接口
除了ApplicationContextAware
和BeanNameAware
之外,Spring 还提供了Aware
接口范围,这些接口使 Bean 向容器指示它们需要某种基础结构依赖性。通常,该名称很好地表明了依赖项类型。下表总结了最重要的Aware
接口:
Name | Injected Dependency | Explained in… |
---|---|---|
ApplicationContextAware
|
声明ApplicationContext 。
|
ApplicationContextAware 和 BeanNameAware |
ApplicationEventPublisherAware
|
附件ApplicationContext 的事件发布者。
|
ApplicationContext 的其他功能 |
BeanClassLoaderAware
|
类加载器,用于加载 Bean 类。 | Instantiating Beans |
BeanFactoryAware
|
声明BeanFactory 。
|
ApplicationContextAware 和 BeanNameAware |
BeanNameAware
|
声明 bean 的名称。 | ApplicationContextAware 和 BeanNameAware |
BootstrapContextAware
|
运行容器的资源适配器BootstrapContext 。通常仅在支持 JCA 的ApplicationContext 实例中可用。
|
JCA CCI |
LoadTimeWeaverAware
|
定义的编织器,用于在加载时处理类定义。 | 在 Spring Framework 中使用 AspectJ 进行加载时编织 |
MessageSourceAware
|
解决消息的已配置策略(支持参数化和国际化)。 | ApplicationContext 的其他功能 |
NotificationPublisherAware
|
Spring JMX 通知发布者。 | Notifications |
ResourceLoaderAware
|
配置的加载程序,用于对资源的低级别访问。 | Resources |
ServletConfigAware
|
当前容器运行的ServletConfig 。仅在可感知网络的 Spring ApplicationContext 中有效。
|
Spring MVC |
ServletContextAware
|
当前容器运行的ServletContext 。仅在可感知网络的 Spring ApplicationContext 中有效。
|
Spring MVC |
再次注意,使用这些接口会将您的代码与 Spring API 绑定在一起,并且不遵循“控制反转”样式。因此,我们建议将它们用于需要以编程方式访问容器的基础结构 Bean。
感谢耐心看到这里的同学,觉得文章对您有帮助的话希望同学们不要吝啬您手中的赞,动动您智慧的小手,您的认可就是我创作的动力!
之后还会勤更自己的学习笔记,感兴趣的朋友点点关注哦。
Spring官方文档解读(五)之自定义 bean 的性质相关推荐
- Spring官方文档中文翻译
准备做个Spring官方文档全翻译专栏以下是大目录, 本翻译是基于Spring5 Core Technologies
- Spring 官方文档彩蛋
Spring 官方文档彩蛋 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句 ...
- Spring官方文档下载
Spring官方文档下载 最近准备弄弄 spring-batch,需要完整的资料,但是大部分都是零散的,于是准备下个官方文档: spring 官方文档下载 官方文档路径:https://docs.sp ...
- 查看spring官方文档
目录 1.基本介绍 2.查看文档 2.1参考手册和API文档有什么区别? 2.2查看参考手册/手册介绍 2.2查看API手册/手册介绍 1.基本介绍 官网网址:spring.io 链接 Spring ...
- gateway的官方文档解读
之前公司用了springcloud的gateway.被一个伙伴留下了一堆的坑,没办法只能从头梳理. 第一步就是确定架构, gateway+consul+springboot 第二步就是确定一个flag ...
- Spring官方文档通读-部分一
Spring 通读官方文档 这部分参考文档涵盖了Spring Framework绝对不可或缺的所有技术. 其中最重要的是Spring Framework的控制反转(IoC)容器.Spring框架的Io ...
- Axon Framework官方文档(五)
5.Command Model 在基于CQRS的应用程序中,一个领域模型(由Eric Evans和Martin Fowler提出的概念)可以是一种非常强大的机制,它可以利用状态更改的验证和执行所涉及的 ...
- 如何下载 spring 官方文档 pdf
spring 系列框架,官方文档都是html 格式的, 并未提供pdf 的下载入口.在html 路径后直接添加/pdf 即可进入pdf 下载页面 1. spring 官网 进入spring官网: ht ...
- Mybatis官方文档解读
跟着Mybatis的官方文档总结一下.~ 简介 什么是mybatis? mybatis是一款优秀的持久层框架,它支持自定义SQL,存储过程以及高级映射.Mybatis免除了所有的JDBC代码以及 设置 ...
最新文章
- iOS10.3 的评论系统
- How React Works (一)首次渲染
- Android工程师面试该怎么准备?终局之战
- 台湾国立大学郭彦甫Matlab教程笔记(8)文件读写
- 程序员面试题精选100题(50)-树的子结构[数据结构]
- 控制 WebBrowser 控件的外观和行为
- 【Python】用 Python 帮财务小妹生成 Excel 报表,小妹直说一辈子。。。
- canvas绘制三角形
- 高效能码农的自我修养:5本书教你怎样科学学习,拒绝无用功
- AD原理图进阶设计1
- Linux CTF 逆向入门
- Excel读取某一列的宏代码VBA代码源码及解说(详尽版)
- 《R语言预测实战》PDF,数据及代码
- Realsense安装使用过程问题汇总
- 阶段3 3.SpringMVC·_04.SpringMVC返回值类型及响应数据类型_8 响应json数据之响应json格式数据...
- 如何激发孩子的想象力_如何培养孩子想象力
- vue项目使用阿里云播放器 Prismplayer 组件,可记录播放时间
- walsh64码 matlab,实验7 Walsh码及单用户CDMA系统直接序列扩频仿真
- unity代码控制物体的透明度总结
- 《算法导论》15章-动态规划 15.1 钢条切割(含有C++代码)