自定义一个bean的本质

本篇讨论如何自定义一个Bean和这个Bean的本质。


一、生命周期回调简介

要与bean生命周期的容器管理进行交互,可以实现Spring InitializingBean和DisposableBean接口。容器要求 afterPropertiesSet()前者和destroy()后者允许bean在初始化和销毁​​bean时执行某些操作。
例如源码中的如下操作:

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

例如如下的这些操作:

在内部,Spring框架使用BeanPostProcessor实现来处理它可以找到的任何回调接口并调用适当的方法。如果您需要自定义功能或其他生命周期行为,Spring不提供开箱即用功能,您可以自行实施BeanPostProcessor。

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

二、详解生命周期回调接口

1. 初始化回调

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

void afterPropertiesSet() throws Exception;

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

  • 方式一:
  • Xml配置
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
  • 实体类
public class ExampleBean {public void init() {// do some initialization work}
}
  • 方式二:不推荐
    Xml配置文件:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>

Java代码:

public class AnotherExampleBean implements InitializingBean {public void afterPropertiesSet() {// do some initialization work}
}

推荐使用方式一,方式一不会将代码耦合到Spring中。

2. 销毁回调

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

void destroy() throws Exception;

建议您不要使用DisposableBean回调接口,因为它不必要地将代码耦合到Spring。或者,使用@PreDestroy注释或指定bean定义支持的通用方法。使用基于XML配置文件,您可以使用destroy-method该属性<bean/>。使用Java配置,您使用的destroyMethod属性@Bean,请参阅 接收生命周期回调。例如,下面的定义:推荐

<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)}
}

destroy-method一个<bean>元素的属性可以被赋予一个特殊的 (inferred)值,指示Spring自动检测特定的bean类(实现或将匹配的任何类)上的公共close或 shutdown方法 。这个特殊 值也可以在元素的属性上 设置,以将此行为应用于整个bean集(请参阅 默认初始化和销毁​​方法)。请注意,这是Java配置的默认行为。java.lang.AutoCloseablejava.io.Closeable(inferred)default-destroy-method<beans>

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

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

你可以为Spring容器配置一种寻找的策略,去每个Bean中寻找初始化和销毁的回调方法的名字。
这意味着,作为应用程序开发人员,您可以编写应用程序类并使用初始化回调函数 init(),而无需为每个bean定义配置init-method=”init”属性。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.");}}
}

xml文件:

<beans default-init-method="init"><bean id="blogService" class="com.foo.DefaultBlogService"><property name="blogDao" ref="blogDao" /></bean></beans>

default-init-method顶层元素属性中的属性的存在导致Spring IoC容器识别init在bean上调用的方法作为初始化方法回调。当一个bean被创建和组装时,如果bean类有这样一个方法,它会在适当的时候被调用。

您可以使用default-destroy-method顶级<beans/>元素上的属性来类似地配置destroy方法回调(即XML) 。

如果现有bean类已经具有与约定不同的回调方法,那么可以通过使用init-method和 自身的destroy-method属性指定方法名称(即XML)来覆盖缺失值<bean/>

Spring容器保证了一个配置好的初始化回调函数在bean被提供了所有的依赖关系后立即被调用。因此初始化回调在原始bean引用上被调用,这意味着AOP拦截器等等还没有被应用到bean。目标bean 首先被完全创建, 然后应用带有其拦截器链的AOP代理(例如)。如果目标bean和代理是分别定义的,那么代码甚至可以绕过代理与原始目标bean进行交互。因此,将拦截器应用于init方法会不一致,因为这样会将目标bean的生命周期与代理/拦截器耦合在一起,并在代码直接与原始目标bean交互时留下奇怪的语义。

4.结合生命周期机制

从Spring 2.5开始,您有三个控制bean生命周期行为的选项:1:the InitializingBeanDisposableBeancallback接口; 2:init()和destroy()方法; 3: @PostConstruct和@PreDestroy 注释。你可以结合这些机制来控制给定的bean。

如果为bean配置了多个生命周期机制,并且每个机制都配置了不同的方法名称,那么每个配置的方法都按照下面列出的顺序执行。但是,如果init()为这些生命周期机制中的多个生命周期机制配置了相同的方法名称(例如, 初始化方法),则该方法将执行一次,如前一部分所述。

为相同的bean配置多种生命周期机制,使用不同的初始化方法,按照以下的顺序调用:

  • 用注释的方法 @PostConstruct
  • afterPropertiesSet()由InitializingBean回调接口定义
  • 自定义配置的init()方法。

销毁方法以相同的顺序被调用:

  • 用注释的方法 @PreDestroy
  • destroy()由DisposableBean回调接口定义
  • 自定义配置的destroy()方法

5.启动和关闭回调

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

public interface Lifecycle {void start();void stop();boolean isRunning();
}

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

public interface LifecycleProcessor extends Lifecycle {void onRefresh();void onClose();
}

注意,LifecycleProcessor它本身就是Lifecycle 界面的扩展。它还添加了两种其他方法来对正在刷新和关闭的上下文作出反应。
请注意,常规org.springframework.context.Lifecycle接口只是显式启动/停止通知的普通合同,并不意味着在上下文刷新时自动启动。考虑实施org.springframework.context.SmartLifecycle对特定bean的自动启动(包括启动阶段)的细粒度控制。另外,请注意,停止通知不保证在销毁之前发生:在正常关闭时,所有Lifecyclebean将在传播一般销毁回调之前首先收到停止通知; 然而,在上下文的生命周期中的热刷新或中止刷新尝试时,只会调用销毁方法。

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

public interface Phased {int getPhase();
}
public interface SmartLifecycle extends Lifecycle, Phased {boolean isAutoStartup();void stop(Runnable callback);
}

启动时,相位最低的物体首先启动,停止时跟随相反的顺序。因此,实现SmartLifecycle并getPhase()返回其方法的对象Integer.MIN_VALUE将是第一个开始和最后一个停止的对象。在频谱的另一端,相位值 Integer.MAX_VALUE将指示对象应该最后开始并且首先停止(可能是因为它取决于要运行的其他进程)。当考虑相位值时,知道任何Lifecycle没有实现的“正常” 对象的默认相位为0也是很重要的。 SmartLifecycle因此,任何负相位值都表示对象应该在这些标准组件之前开始(并且在他们),反之亦然,任何正相位值。

正如你所看到的定义的停止方法SmartLifecycle接受回调。任何实现必须run()在该实现的关闭过程完成后调用该回调的方法。这可以在必要时启用异步关闭,因为LifecycleProcessor接口 的默认实现DefaultLifecycleProcessor将等待每个阶段中的对象组的超时值来调用该回调。每个阶段的默认超时时间是30秒。您可以通过在上下文中定义一个名为“lifecycleProcessor”的bean来覆盖默认的生命周期处理器实例。如果您只想修改超时值,那么定义以下就足够了:

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

如前所述,LifecycleProcessor接口定义了用于刷新和关闭上下文的回调方法。后者将简单地驱动关闭进程,就好像stop()被明确地调用一样,但是当关闭时会发生。另一方面,’刷新’回调启用了SmartLifecycle豆的另一个功能 。当上下文刷新时(在所有对象被实例化和初始化之后),该回调将被调用,并且在那时默认生命周期处理器将检查每个SmartLifecycle对象的isAutoStartup()方法返回的布尔值 。如果是“真”,那么该对象将在该点开始,而不是等待显式调用上下文或它自己的start()方法(与上下文刷新不同,上下文启动不会自动为标准上下文实现发生)。“阶段”值以及任何“依赖”关系将以与上述相同的方式确定启动顺序。

6.在非web应用程序中正常关闭Spring IoC容器

本节仅适用于非Web应用程序。ApplicationContext当相关的Web应用程序关闭时,Spring的基于Web的 实现已经有适当的代码来正常关闭Spring IoC容器。

如果您在非Web应用程序环境中使用Spring的IoC容器,例如,在富客户端桌面环境中; 您使用JVM注册了一个关闭钩子。这样做可以确保正常关闭并在单例bean上调用相关的销毁方法,从而释放所有资源。当然,您仍然必须正确配置和实施这些销毁回调。
要注册关闭挂钩,请调用接口registerShutdownHook()上声明的方法ConfigurableApplicationContext:

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...}
}

7.ApplicationContextAware和BeanNameAware

当ApplicationContext创建一个实现该org.springframework.context.ApplicationContextAware接口的对象实例时,该实例提供了对该 接口ApplicationContext的引用。

public interface ApplicationContextAware {void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

因此,bean可以通过编程操作ApplicationContext来创建它们,通过ApplicationContext接口,或者通过将引用强制转换为此接口的已知子类,例如ConfigurableApplicationContext,可以公开其他功能。一个用途是对其他bean的程序化检索。有时候这种能力是有用的; 然而,通常你应该避免它,因为它将代码耦合到Spring,并且不遵循Inversion of Control风格,其中协作者被提供给bean作为属性。ApplicationContext提供对文件资源的访问,发布应用程序事件和访问文件的其他方法 MessageSource。这些附加功能在ApplicationContext的附加功能中进行了描述。

自Spring 2.5起,自动装配是另一种可供参考的选择 ApplicationContext。“传统” constructor和byType自动装配模式(如自动装配协作者所述)可以分别为ApplicationContext构造函数参数或setter方法参数提供类型的依赖关系 。为了获得更大的灵活性,包括自动装配字段和多个参数方法的能力,请使用新的基于注释的自动装配功能。如果这样做,ApplicationContext则自动装入到字段,构造函数参数或方法参数中,ApplicationContext如果所涉及的字段,构造函数或方法携带@Autowired注释,则该参数将期望该类型。有关更多信息,请参阅 @Autowired。

当ApplicationContext创建一个实现 org.springframework.beans.factory.BeanNameAware接口的类时,该类将被提供对其关联对象定义中定义的名称的引用。

public interface BeanNameAware {void setBeanName(String name) throws BeansException;
}

这个回调函数是在正常bean属性填充之后,但在初始化回调函数(如InitializingBean afterPropertiesSet或自定义init方法)之前调用的。

8.其他Aware接口

除了上面谈到的ApplicationContextAwareBeanNameAware。Spring提供了一系列的Aware,允许bean指示,他们需要一定的容器接口基础设施的依赖。最重要的Aware接口总结如下 - 作为一般规则,名称是依赖类型的良好指示:
其他的依赖解释:

Name Injected Dependency(依赖注入) Explained in…​(解释它)
ApplicationContextAware Declaring(声明) ApplicationContext ApplicationContextAware and BeanNameAware
ApplicationEventPublisherAware 封闭的事件发布者 ApplicationContext ApplicationContext的附加功能
BeanClassLoaderAware 用于加载Bean类的类加载器。 实例化bean
BeanFactoryAware 声明 BeanFactory。 ApplicationContextAware和BeanNameAware
BeanNameAware 声明bean的名称 ApplicationContextAware和BeanNameAware
BootstrapContextAware BootstrapContext容器运行的资源适配器。通常仅在支持JCA的ApplicationContexts中可用。 JCA CCI
LoadTimeWeaverAware 定义编织器用于在加载时处理类定义。 在Spring框架中使用AspectJ加载时织入
MessageSourceAware 用于解析消息的配置策略(支持参数化和国际化) ApplicationContext的附加功能
NotificationPublisherAware Spring JMX通知发布者。 通知
ResourceLoaderAware 配置的加载器可以实现对资源的低级访问。 资源
ServletConfigAware 当前ServletConfig容器运行英寸有效只在一个网络意识的Spring ApplicationContext Spring MVC
ServletContextAware 当前ServletContext容器运行英寸有效只在一个网络意识的春天 ApplicationContext Spring MVC

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

好啦,今天的官网文档解析就到这么多了。

SpringFramework核心技术一(IOC:自定义一个bean的本质)相关推荐

  1. spring IOC 装配一个bean

    1.0属性注入 新建一个people类 package com.java.test3;/*** @author nidegui* @create 2019-06-22 14:45*/ public c ...

  2. SpringFramework核心技术一(IOC:命名bean)

    命名Bean 每个bean都有一个或多个标识符.这些标识符在托管bean的容器内必须是唯一的.一个bean通常只有一个标识符,但是如果它需要多个标识符,额外的标识符可以被认为是别名. 一.如何命名Be ...

  3. Spring核心技术之IOC容器(一):IOC容器与Bean简介

    最近开始研究Spring框架,今天学习Spring的核心内容IOC 与 Bean 1. Spring IOC 与 Bean 简介  Inversion of Control (IoC)即控制反转,也叫 ...

  4. SpringFramework核心技术一(IOC:ApplicationContext的附加功能)

    标题 正如本章介绍中所讨论的,该org.springframework.beans.factory 包提供了用于管理和操作bean的基本功能,包括以编程方式.该org.springframework. ...

  5. IoC-spring 的灵魂(带你轻松理解IOC思想及bean对象的生成过程)

    在理解任何技术之前,我都会问自己一个问题:它的产生是为了解决什么样的问题,以及如何解决这些问题?希望你能在本篇文章中找到答案-- (由于大家对Ioc应该是经常使用了,所以这里不会告诉你应该怎么样使用, ...

  6. IOC容器和Bean的配置实例

    实验1: <!--实验1:通过IOC容器创建对象,并为属性赋值 --> <!-- 需要由IOC容器创建对象的全类名 --> <!-- 为了便于从IOC容器中获取book对 ...

  7. Spring IOC容器和Bean的配置

    Spring IOC容器 和Bean的配置 : IOC和DI IOC(Inversion of Control):反转控制 在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的 ...

  8. IOC容器和Bean的配置

     IOC容器和Bean的配置   1        IOC和DI ①IOC(Inversion of Control):反转控制. 在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取 ...

  9. Spring IoC容器与Bean管理

    Spring IoC容器与Bean管理 一.Spring IoC容器与Bean管理 1.Spring快速入门 IoC控制反转 DI依赖注入 Spring概述 Spring IoC初体验 使用XML方式 ...

最新文章

  1. ERD2005中文版
  2. php 数据显示,数据显示处理,该怎么处理
  3. LRN和Batch Norm
  4. 小程序 省市区县三级联动选择器(caseCade)
  5. 8 SystemVerilog语言编写UART发送
  6. iQOO 8首次采用三星E5屏幕:2021年最好的手机屏幕
  7. Kaggle比赛(二)House Prices: Advanced Regression Techniques
  8. BootStrap的应用——实现黑马旅游网页面
  9. Tesla M40 24G 在Win11上的双显卡显示实现、改风冷
  10. 在项目中怎样写故障树或者类层次
  11. 触摸精灵脚本使用snapshotScreen截图错误
  12. 6岁就获吉尼斯世界纪录!这届10后程序员「小鬼当家」
  13. 程序员的双肩包,大概能装下整个宇宙!
  14. 分布式数据结构与算法面试题
  15. 区块链DApp从零开始学 (二) | 超详细 DApp创建 | 发行代币token | 宠物领养
  16. 【R语言】RMarkdown使用
  17. java工作流框架jbpm_【Java EE 学习 67 上】【OA项目练习】【JBPM工作流的使用】
  18. linux网卡 命令 ncmil,Linux常用性能检测命令解释
  19. 线程池七个参数的含义
  20. ubnutu下载网易云音乐

热门文章

  1. CH455 数码管驱动以及键盘控制芯片 应用笔记
  2. php laravel lumen 快速接入网易云信 im
  3. 斐讯k2刷不死breed K2 22.5.11.14
  4. Web端H.265播放器研发解密 1
  5. Spark创建DataFrame
  6. composer 指定PHP版本
  7. SpringBoot集成原生rocketmq-client
  8. 各式 Web 前端開發工具整理
  9. 数据中心机房基础建设,等级、机柜、机架设定等相关内容都在这里!
  10. 【转】苹果警告中国开发者:不要绕过APP反追踪功能