Bootstrap

Spring 5 中文解析核心篇-IoC容器之自定义Bean性质

框架提供一些接口,你可以使用这些接口去自定义的性质。这个章节包括下面内容:

###### 1.6.1 生命周期回调

为了与容器的的生命周期的管理交互,你可以实现提供的和接口。容器为前者调用并为后者调用,以使在初始化和销毁时执行某些操作。

在现代化的应用中,的和注解是一般被考虑的最好实践去接收生命周期回调。使用这些注解意味着你的这些不需要耦合规范接口。更多详情,查看

>

如果你不想去使用注解,但是你仍然想移除耦合,考虑bean的元数据定义。

内部地,框架使用实现去处理任何的回调接口,它能找到和调用适合的方法。如果你需要定制特性或者其他的生命周期行为默认没有提供,你需要自己实现一个。关于更多的信息,查看

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

生命周期回调接口在这章节中描述。

  • 初始化回调

接口允许在的所有属性被设置之前执行初始化工作。接口有一个简单方法:

 void afterPropertiesSet() throws Exception;

我们推荐你不要去使用接口,因为它不必要的耦合。或者,我们建议使用注解或者指定一个初始化方法。在基于配置元数据实例中,你可以使用属性去指定方法的名称并且方法没有返回值和无参数签名。在中,你可以使用的属性。查看[接收生命周期回调](https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#beans-java-lifecycle-callbacks)。考虑下面的例子:

 

  public class ExampleBean {

  

      public void init() {

          // do some initialization work

      }

  }

上面的例子几乎和下面的例子完全一样(由两个清单组成):

  

  public class AnotherExampleBean implements InitializingBean {

  

      @Override

      public void afterPropertiesSet() {

          // do some initialization work

      }

  }

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

> 参考代码:

  • 销毁回调

当包含的容器被销毁时,实现接口的获得回调。接口有个简单方法:

 void destroy() throws Exception;

我们推荐你不要使用回调接口,因为它不必要的耦合代码。或者,我们建议使用 注解或者指定一个通用方法通过的定义支持。基于的配置元数据,你可以在上使用属性。通过配置,你可以使用的属性。查看[接收生命周期回调](https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#beans-java-lifecycle-callbacks)。考虑下面的定义:

 

  public class ExampleBean {

  

      public void cleanup() {

          // do some destruction work (like releasing pooled connections)

      }

  }

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

 

  public class AnotherExampleBean implements DisposableBean {

  

      @Override

      public void destroy() {

          // do some destruction work (like releasing pooled connections)

      }

  }

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

> 你可以赋值元素的属性值,这指示自动的在类上检测一个公共的或者方法。(因此,任何实现或的类都将匹配)你可以在元素的属性设置这个指定的值去应用这个行为到整个容器(查看)。注意:这个一个默认行为在中。

> 参考代码:

  • 默认初始化和销毁方法

当你写初始化和销毁方法回调的时候,不要使用指定的和回调接口,典型的命名例如:、、等等。理想地,生命周期回调方法的命名在项目中被标注化,因此所有的开发者使用项目的名字确保一致性。

你可以配置框架容器去在每个查找被命名初始化和销毁回调方法名称。作为一个应用开发者,这意味着你可以写你的应用类和使用初始化回调调用,没有必要配置每个的定义属性。当被创建时(并按照之前描述的标准生命周期回调),容器调用方法。这个特性强调为初始化和销毁回调方法命名的一致性。

假设你的初始化回调方法被命名为并且你的销毁回调方法被命名为。你的类像下面例子的类:

 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.");

          }

      }

  }

然后,你可以在类似于以下内容的中使用该类:

 

  

      

          

      

  

  

顶层元素属性中描述导致容器在的类上去识别一个叫做的方法作为初始化方法回调。当被创建和组装好,如果的类有一个方法,它在适当的时候被调用。

你可以配置销毁方法回调类似(在)的通过使用在顶层元素上的方法。

现有的类已经具有回调方法的名称,这些方法的命名方式与约定不符,你可以通过元素属性和指定方法名称。

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

  • 组合生命周期机制

从Spring2.5后,你有三种可以选择的方式去控制生命周期行为:

* 和[](https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#beans-factory-lifecycle-disposablebean)回调接口

* 自定义和

* .。你可以组合这些机制去控制。

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

为同一个配置的多种生命周期机制具有不同的初始化方法,如下所示:

1. 方法注解

2. 通过回调接口定义

3. 自定义配置 方法

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

1. 方法注解

2. 通过回调接口定义

3. 自定义配置方法

  • 启动和关闭回调

接口定义了必须要的方法为这些生命周期所必须(例如:启动和停止一些后台处理)。

  public interface Lifecycle {

  

      void start();

  

      void stop();

  

      boolean isRunning();

  }

任何管理的对象可能实现接口。然而,当接收到启动和停止信号(例如:在运行时停止和重启场景),它将这些调用级联到在该上下文中定义的所有生命周期实现。通过代理到处理,在下面清单显示:

  public interface LifecycleProcessor extends Lifecycle {

  

      void onRefresh();

  

      void onClose();

  }

注意:是接口的拓展。这个接口增加了两个额外的方法去接收上下文的刷新和关闭。

> 注意:

>

> 常规的。生命周期接口是显式启动和停止通知的普通契约,并不意味着在上下文刷新时自动启动。为了更细粒度控制的自动启动(包括启动阶段),考虑使用替换。

>

> 停止通知不会被保证在销毁之前到来。在常规的关闭上,所有的第一次接受停止通知在销毁回调被传播之前。但是,在上下文生命周期中进行热更新或更新尝试失败时,仅调用方法。

启动和关闭顺序调用是非常重要的。如果依赖关系存在任何对象之间,依赖侧开始在被依赖之后并且它的停止在它的依赖之前。然而,在运行时,这个直接依赖是未知的。你可能只知道某种类型的对象应该先于另一种类型的对象开始。在这种情况下,接口定义其他可选,即方法在它的父接口被定义,。下面的清单显示Phased接口的定义。

  public interface Phased {

  

      int getPhase();

  }

下面清单显示接口定义

  public interface SmartLifecycle extends Lifecycle, Phased {

  

      boolean isAutoStartup();

  

      void stop(Runnable callback);

  }

启动时,阶段值最低的对象首先启动。停止时,顺序相反。因此,实现接口并且方法返回的对象将在第一个启动和最后一个停止。另一种类型,阶段值指示这个对象最后一个被启动并且第一个被销毁 (可能因为它依赖其他处理运行)。当考虑这个阶段值的时候,重要的是要知道,任何未实现的“正常”生命周期对象的默认阶段为。因此,任何负的阶段值表示对象应该启动在这些标准的组件之前(停止在它们之后)。对于任何正阶段值,情况正好相反。

停止方法通过接受一个回调被定义。任何实现必须在实现的关闭处理完成后调用回调的方法。这将在必要时启用异步关闭,因为接口的默认实现会等待其超时值,以等待每个阶段内的对象组调用该回调。每个阶段默认超时时间30秒。你可以通过定义一个 命名为在上下文中覆盖这个默认生命周期处理实例。如果你想仅仅修改超时时间,定义以下内容就足够了:

  

      

      

  

像前面提到的,接口定义回调方法为上下文更好的刷新和关闭。后者驱动关闭过程,就好像已经显式调用了一样,但是它在上下文关闭时发生。另一方面,“”回调启用了 的另一个特性。当这个上下文被刷新(所有的被初始化和实例化),即回调被调用。在那个阶段,默认的生命周期处理器通过每个对象的方法检测返回值。如果,对象在那个阶段被启动而不是等待上下文或者自身的方法显示调用(与上下文刷新不同,对于标准上下文实现,上下文启动不会自动发生)。像前面所描述的,值和任何依赖关系确定了启动顺序。

> 参考代码:

  • 在非Web应用程序中优雅关闭Spring IoC容器

> 这个部分仅仅适用非应用程序。的基于的实现提供优雅的关闭容器,当相关的应用程序关闭的时候。

如果你使用的容器在非应用环境(例如,在一个富客户端桌面环境),在注册一个关闭钩子。这样做确保优雅的关闭和调用在单例上的销毁方法,因此所有资源被释放。你必须仍然正确地配置和实现这些销毁回调。

去注册一个关闭钩子,调用方法是在接口上声明,如下例子:

  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");

  

          //增加一个Hook构造回调

          ctx.registerShutdownHook();

  

          // app runs here...

  

          // main method exits, hook is called prior to the app shutting down...

      }

  }

###### 1.6.2 和

当创建一个对象实例同时实现了接口,这个实例提供一个对的引用。下面的清单显示了接口的定义:

public interface ApplicationContextAware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

因此,这些可以编程地操作创建它们,通过接口或者转换引用为已知的这个接口的子类(例如:,它暴露了附加的功能)。一种用途是通过编程方式检索其他。有时候这个能力非常有用。然而,一般的,你应该避免它,因为它耦合的代码并且不遵循控制反转格式,将协调者作为的属性。其他方法提供获取资源文件、发布应用事件和访问。这些额外的特性

自动装配是获得对的引用的另一种选择。传统的和自动装配模式()能够为构造函数或者方法参数提供一个类型的依赖。为了获得更大的灵活性,包括自动装配字段和多个参数方法的能力,可以使用基于注解的自动装配特性。如果这样做,自动装配到字段、构造参数或方法参数,如果字段、构造函数或方法带有注解那么该参数期望类型。更多的信息,查看使用[@Autowired](https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#beans-autowired-annotation)。

当创建一个类并且实现接口时,该类将获得对其关联对象定义中定义的名称的引用。下面清单显示接口定义:

public interface BeanNameAware {

    void setBeanName(String name) throws BeansException;
}

这个回调在被填充之后被调用,但是在初始化回调之前例如, 或者自定义。

参考代码:

###### 1.6.3 其他Aware接口

除了和之外,提供了广泛的回调接口,这些接口使向容器指示它们需要一些基础结构依赖性。作为基本规则,这个名字指示依赖类型。下面的表格总结最重要的接口:

再次注意,使用这些接口关联到你的代码到并且不遵循控制反转的风格。我们推荐使用基础设施需要编程的去容器获取。

作者

个人从事金融行业,就职过易极付、思建科技、某网约车平台等重庆一流技术团队,目前就职于某银行负责统一支付系统建设。自身对金融行业有强烈的爱好。同时也实践大数据、数据存储、自动化集成和部署、分布式微服务、响应式编程、人工智能等领域。同时也热衷于技术分享创立公众号和博客站点对知识体系进行分享。

博客地址: http://youngitman.tech

CSDN: https://blog.csdn.net/liyong1028826685

微信公众号: