SpringCore笔记(二)

1.5. Bean 作用域

创建 bean 定义时,您创建了一个配方,用于创建由该 bean 定义定义的类的实际实例。bean 定义是一个配方的想法很重要,因为这意味着,与类一样,您可以从单个配方创建许多对象实例。

您不仅可以控制要插入到从特定 bean 定义创建的对象中的各种依赖项和配置值,还可以控制从特定 bean 定义创建的对象的范围。这种方法功能强大且灵活,因为您可以通过配置选择您创建的对象的范围,而不必在 Java 类级别烘焙对象的范围。可以将 Bean 定义为部署在多个范围之一中。Spring Framework 支持六个范围,其中四个仅在您使用 web-aware ApplicationContext时才可用。您还可以创建自定义范围。

下表描述了支持的范围:
表 3. Bean 范围

Scope Description
single (默认)将单个 bean 定义范围限定为每个 Spring IoC 容器的单个对象实例。
prototype 将单个 bean 定义范围限定为任意数量的对象实例。
request 将单个 bean 定义范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有自己的 bean 实例,该 bean 实例是在单个 bean 定义的后面创建的。仅在 web-aware Spring 的上下文中有效ApplicationContext。
session 将单个 bean 定义范围限定为 HTTP 的生命周期Session。仅在 web-aware Spring 的上下文中有效ApplicationContext。
application 将单个 bean 定义范围限定为ServletContext. 仅在 web-aware Spring 的上下文中有效ApplicationContext。
websocket 将单个 bean 定义范围限定为WebSocket. 仅在 web-aware Spring 的上下文中有效ApplicationContext。

从 Spring 3.0 开始,线程作用域可用,但默认情况下未注册。有关更多信息,请参阅 的文档SimpleThreadScope。有关如何注册此或任何其他自定义范围的说明,请参阅使用自定义范围。

1.5.1. 单例作用域

只有一个单例 bean 的共享实例被管理,并且所有对带有一个或多个 ID 与该 bean 定义匹配的 bean 的请求都会导致 Spring 容器返回一个特定的 bean 实例。

换句话说,当您定义一个 bean 定义并且它的作用域是一个单例时,Spring IoC 容器会创建该 bean 定义定义的对象的一个实例。该单个实例存储在此类单例 bean 的缓存中,并且对该命名 bean 的所有后续请求和引用都返回缓存对象。下图显示了单例范围的工作原理:


image.png

Spring 的单例 bean 概念不同于四人组 (GoF) 模式书中定义的单例模式。GoF 单例对对象的范围进行了硬编码,以便每个 ClassLoader 只创建一个特定类的一个实例。Spring 单例的范围最好描述为每个容器和每个 bean。这意味着,如果您在单个 Spring 容器中为特定类定义一个 bean,则 Spring 容器会创建该 bean 定义定义的类的一个且仅一个实例。单例作用域是 Spring 中的默认作用域。要将 bean 定义为 XML 中的单例,您可以定义一个 bean,如下例所示:





1.5.2. 原型作用域

bean 部署的非单一原型范围导致每次对特定 bean 发出请求时都会创建一个新 bean 实例。也就是说,bean 被注入到另一个 bean 中,或者您通过getBean()容器上的方法调用来请求它。通常,您应该对所有有状态 bean 使用原型作用域,对无状态 bean 使用单例作用域。

下图说明了 Spring 原型范围:


image.png

(数据访问对象 (DAO) 通常不配置为原型,因为典型的 DAO 不保存任何会话状态。我们更容易重用单例图的核心。)

以下示例将 bean 定义为 XML 中的原型:


与其他作用域相比,Spring 不管理原型 bean 的完整生命周期。容器实例化、配置和以其他方式组装原型对象并将其交给客户端,没有该原型实例的进一步记录。因此,尽管在所有对象上调用初始化生命周期回调方法,而不管范围如何,但在原型的情况下,不会调用配置的销毁生命周期回调。客户端代码必须清理原型范围内的对象并释放原型 bean 持有的昂贵资源。要让 Spring 容器释放原型作用域 bean 持有的资源,请尝试使用自定义bean post-processor,它保存对需要清理的 bean 的引用。

在某些方面,Spring 容器在原型作用域 bean 方面的角色是 Javanew运算符的替代品。超过该点的所有生命周期管理都必须由客户端处理。有关 Spring 容器中 bean 生命周期的详细信息,请参阅生命周期回调。

1.5.3. 具有 Prototype-bean 依赖关系的 Singleton Bean

当您使用具有对原型 bean 的依赖的单例作用域 bean 时,请注意在实例化时解析依赖关系。因此,如果您将原型范围的 bean 依赖注入到单例范围的 bean 中,则会实例化一个新的原型 bean,然后将依赖项注入到单例 bean 中。原型实例是唯一提供给单例作用域 bean 的实例。

但是,假设您希望单例范围的 bean 在运行时重复获取原型范围的 bean 的新实例。您不能将原型范围的 bean 依赖注入到您的单例 bean 中,因为该注入仅发生一次,当 Spring 容器实例化单例 bean 并解析并注入其依赖项时。如果您在运行时多次需要原型 bean 的新实例,请参阅方法注入。

1.5.4. 请求、会话、应用程序和 WebSocket 范围

在request,session,application,和websocket范围只有当你使用一个基于web的Spring可ApplicationContext实现(例如 XmlWebApplicationContext)。如果将这些作用域与常规 Spring IoC 容器(例如 ClassPathXmlApplicationContext)一起使用,则会抛出抱怨未知 bean 作用域的 IllegalStateException。

初始 Web 配置

为了支持Bean的范围界定在request,session,application,和 websocket(即具有web作用域bean),需要做少量的初始配置定义你的Bean之前。(标准范围不需要此初始设置:singleton和prototype。)

您如何完成此初始设置取决于您的特定 Servlet 环境。

如果您在 Spring Web MVC 中访问作用域 bean,实际上是在 Spring DispatcherServlet处理的请求中,不需要特殊设置。 DispatcherServlet已经暴露了所有相关的状态。

如果您使用 Servlet 2.5 Web 容器,并且请求在 Spring 之外处理 DispatcherServlet(例如,使用 JSF 或 Struts 时),则需要注册 org.springframework.web.context.request.RequestContextListener ServletRequestListener. 对于 Servlet 3.0+,这可以通过使用WebApplicationInitializer 接口以编程方式完成。或者,或者对于较旧的容器,请将以下声明添加到您的 Web 应用程序的web.xml文件中:


    ...
    
        
            org.springframework.web.context.request.RequestContextListener
        
    
    ...

或者,如果您的侦听器设置存在问题,请考虑使用 Spring 的 RequestContextFilter. 过滤器映射取决于周围的 Web 应用程序配置,因此您必须适当地更改它。以下清单显示了 Web 应用程序的过滤器部分:


    ...
    
        requestContextFilter
        org.springframework.web.filter.RequestContextFilter
    
    
        requestContextFilter
        /*
    
    ...

DispatcherServlet, RequestContextListener, 和RequestContextFilter都做完全相同的事情,即将 HTTP 请求对象绑定到Thread为该请求提供服务的对象。这使得请求和会话范围内的 bean 在调用链的更下游可用。

请求范围

考虑 bean 定义的以下 XML 配置:


Spring 容器通过loginAction对每个 HTTP 请求使用bean 定义来创建bean LoginAction的新实例。也就是说, loginActionbean 的范围在 HTTP 请求级别。您可以根据需要更改所创建实例的内部状态,因为从同一loginActionbean 定义创建的其他实例看不到这些状态更改。它们是针对个人要求的。当请求完成处理时,该请求范围内的 bean 将被丢弃。

当使用注解驱动的组件或 Java 配置时,@RequestScope注解可用于将组件分配给request作用域。以下示例显示了如何执行此操作:

@RequestScope
@Component
public class LoginAction {
    // ...
}

Session Scope

考虑 bean 定义的以下 XML 配置:


Spring 容器通过在userPreferences单个 HTTP 的生命周期中使用Session bean定义来创建UserPreferences bean的新实例。换句话说,userPreferencesbean 有效地限定在 HTTPSession级别。与请求范围的 bean 一样,您可以根据需要更改所创建实例的内部状态,因为知道其他Session也在使用从相同userPreferencesbean 定义创建的实例的HTTP实例不会看到这些状态更改,因为它们特定于单个 HTTP Session。当 HTTPSession最终被丢弃时,作用域为该特定 HTTP 的 bean Session也将被丢弃。

当使用注解驱动的组件或 Java 配置时,您可以使用 @SessionScope注解将组件分配给session作用域。以下示例显示了如何执行此操作:

@SessionScope
@Component
public class UserPreferences {
    // ...
}

Application Scope

Consider the following XML configuration for a bean definition:


Spring 容器通过appPreferences对整个 Web 应用程序使用一次 bean 定义来创建bean AppPreferences的新实例。也就是说, appPreferencesbean 的作用域在ServletContext级别并存储为常规 ServletContext属性。这有点类似于 Spring 单例 bean,但在两个重要方面有所不同:它是一个单例 per ServletContext,而不是 per Spring ApplicationContext(在任何给定的 Web 应用程序中可能有多个),并且它实际上是公开的,因此作为一个ServletContext属性可见.

当使用注解驱动的组件或 Java 配置时,您可以使用 @ApplicationScope注解将组件分配给application作用域。以下示例显示了如何执行此操作:

@ApplicationScope
@Component
public class AppPreferences {
    // ...
}

有作用域 Bean 作为依赖项

Spring IoC 容器不仅管理对象(bean)的实例化,还管理协作者(或依赖项)的连接。如果您想将(例如)一个 HTTP 请求范围的 bean 注入到另一个生命周期更长的 bean 中,您可以选择注入一个 AOP 代理来代替该范围的 bean。也就是说,您需要注入一个代理对象,该对象公开与作用域对象相同的公共接口,但也可以从相关作用域(例如 HTTP 请求)中检索真实目标对象,并将方法调用委托给真实对象。

  • 您还可以使用这范围的bean如果单例,然后引用通过一个可序列化的中间代理,因此能够在反序列化时重新获取目标单例 bean。

  • 针对 scope 的 bean声明prototype时,共享代理上的每个方法调用都会导致创建一个新的目标实例,然后将调用转发到该实例。

  • 此外,范围代理并不是以生命周期安全的方式从较短范围访问 bean 的唯一方法。您还可以将您的注入点(即构造函数或 setter 参数或自动装配字段)声明为ObjectFactory,允许getObject()每次需要时调用以按需检索当前实例 - 无需保留实例或单独存储它。

  • 作为扩展变体,您可以声明ObjectProvider,它提供了几个额外的访问变体,包括getIfAvailablegetIfUnique

  • 其 JSR-330 变体被调用,Provider并与Provider 声明和get()每次检索尝试的相应调用一起使用。有关JSR-330 整体的更多详细信息,请参见beans-standard-annotations。

下例中的配置只有一行,但了解其背后的“为什么”以及“如何”很重要:




    
    
        
         
    

    
    
        
        
    

要创建这样的代理,请将子元素插入到作用域 bean的定义中(请参阅选择要创建的代理类型和基于 XML 模式的配置)。为何定义Bean的作用域requestsession和自定义范围级别必须在元素中?考虑以下单例 bean 定义,并将其与您需要为上述范围定义的内容进行对比(请注意,以下 userPreferencesbean 定义不完整):




    

在前面的示例中,单例 bean ( userManager) 被注入了对 HTTPSession范围 bean ( userPreferences)的引用。这里的重点是 userManagerbean 是一个单例:它在每个容器中被实例化一次,并且它的依赖项(在这种情况下只有一个,userPreferencesbean)也只注入一次。这意味着userManagerbean 仅对完全相同的userPreferences对象(即最初注入它的对象)进行操作。

这不是您将生命周期较短的作用域 bean 注入到生命周期较长的作用域 bean 中时想要的行为(例如,将 HTTPSession作用域的协作 bean 作为依赖项注入到单例 bean 中)。相反,您需要一个userManager 对象,并且在 HTTP 的生命周期内Session,您需要一个userPreferences特定于 HTTP的对象Session。因此,容器创建一个对象,该对象公开与类完全相同的公共接口UserPreferences(理想情况下是一个UserPreferences实例的对象),它可以UserPreferences从作用域机制(HTTP 请求,Session等等)中获取真正的 对象。容器将这个代理对象注入到userManagerbean 中,它不知道这个UserPreferences引用是一个代理。在这个例子中,当一个 UserManager实例调用依赖注入UserPreferences 对象上的方法,它实际上是调用代理上的方法。然后代理 UserPreferences从(在这种情况下)HTTPSession获取真实UserPreferences对象并将方法调用委托给检索到的真实对象。

因此,你当注射需要满足以下(正确和完整)的配置 request-和session-scoped豆类为合作对象,如下例所示:

选择要创建的代理类型

默认情况下,当 Spring 容器为标记有元素的 bean 创建代理时,会创建一个基于 CGLIB 的类代理。

CGLIB 代理只拦截public方法调用!不要在这样的代理上调用非公共方法。它们不会委托给实际作用域的目标对象。

或者,您可以配置 Spring 容器,通过指定元素的proxy-target-class属性值false,为此类作用域 bean 创建标准的基于 JDK 接口的代理。使用基于 JDK 接口的代理意味着您不需要应用程序类路径中的其他库来影响此类代理。然而,这也意味着作用域 bean 的类必须至少实现一个接口,并且注入作用域 bean 的所有协作者都必须通过其接口之一引用该 bean。以下示例显示了基于接口的代理:



    



    

有关选择基于类或基于接口的代理的更多详细信息,请参阅代理机制。

1.5.5. Custom Scopes

bean 作用域机制是可扩展的。您可以定义自己的范围,甚至重新定义现有范围,尽管后者被认为是不好的做法,并且您不能覆盖内置范围singleton和prototype范围。

创建自定义范围

要将自定义范围集成到 Spring 容器中,您需要实现org.springframework.beans.factory.config.Scope本节中描述的 接口。有关如何实现您自己的范围的想法,请参阅Scope Spring Framework 本身提供的实现和Scope javadoc,其中更详细地解释了您需要实现的方法。

Scope接口有四种方法可以从作用域中获取对象,将它们从作用域中移除,以及让它们被销毁。

例如,会话作用域实现返回会话作用域 bean(如果它不存在,则该方法在将它绑定到会话以供将来参考后返回该 bean 的一个新实例)。以下方法从底层范围返回对象:

Object get(String name, ObjectFactory objectFactory)

例如,会话范围实现从底层会话中删除会话范围的 bean。应该返回该对象,但如果没有找到具有指定名称的对象,您可以返回null。以下方法从基础范围中删除对象:

Object remove(String name)

以下方法注册了一个回调,当它被销毁或范围中的指定对象被销毁时,该范围应该调用该回调:

void registerDestructionCallback(String name, Runnable destructionCallback)

有关 销毁回调的更多信息,请参阅javadoc或 Spring 范围实现。

以下方法获取基础范围的对话标识符:

String getConversationId()

这个标识符对于每个范围都是不同的。对于会话范围的实现,此标识符可以是会话标识符。

使用自定义范围

在编写并测试一个或多个自定义Scope实现之后,您需要让 Spring 容器知道您的新范围。以下方法是Scope向 Spring 容器注册 new 的核心方法:

void registerScope(String scopeName, Scope scope);

此方法在ConfigurableBeanFactory接口上声明,可通过 Spring 附带的BeanFactory大多数具体ApplicationContext实现的属性获得。

该registerScope(..)方法的第一个参数是与作用域关联的唯一名称。Spring 容器本身中此类名称的示例是singleton和 prototype。该registerScope(..)方法的第二个参数是Scope您希望注册和使用的自定义实现的实际实例。

假设您编写了自定义Scope实现,然后按照下一个示例所示进行注册。

下一个示例使用SimpleThreadScope,它包含在 Spring 中,但默认情况下未注册。对于您自己的自定义Scope 实现,说明是相同的。

Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);

然后,您可以创建遵守 自定义Scope规则的 bean 定义, Scope如下所示:


使用自定义Scope实现,您不仅限于以编程方式注册范围。您还可以使用CustomScopeConfigurer类以声明方式进行注册 Scope,如以下示例所示:




    
        
            
                
                    
                
            
        
    

    
        
        
    

    
        
    


当您在FactoryBean实现中声明放置,作用域是工厂 bean 本身,而不是从getObject().

1.6. 自定义 Bean 的性质

pring Framework 提供了许多可用于自定义 bean 性质的接口。本节将它们分组如下:

  • Lifecycle Callbacks

  • ApplicationContextAwareBeanNameAware

  • 其他Aware接口

1.6.1. Lifecycle Callbacks

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

JSR-250@PostConstruct@PreDestroy注解通常被认为是在现代 Spring 应用程序中接收生命周期回调的最佳实践。使用这些注解意味着您的 bean 不会耦合到 Spring 特定的接口。有关详细信息,请参阅使用@PostConstruct@PreDestroy

如果您不想使用 JSR-250 注释但仍想移除耦合,请考虑init-methoddestroy-methodbean 定义元数据。

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

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

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

初始化回调

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

void afterPropertiesSet() throws Exception;

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


public class ExampleBean {

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

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


public class AnotherExampleBean implements InitializingBean {

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

但是,前面两个示例中的第一个没有将代码耦合到 Spring。

销毁回调

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

void destroy() throws Exception;

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


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。

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

默认初始化和销毁方法

当您编写不使用 Spring 特定InitializingBeanDisposableBean回调接口的初始化和销毁方法回调时,您通常编写具有诸如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 中使用该类:



    
        
    


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

您可以通过使用default-destroy-method顶级元素上的属性类似地(即在 XML 中)配置销毁方法回调 。

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

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

结合生命周期机制

从 Spring 2.5 开始,您可以通过三个选项来控制 bean 生命周期行为:

  • InitializingBeanDisposableBean回调接口

  • 自定义init()destroy()方法

  • @PostConstruct@PreDestroy 注释。您可以组合这些机制来控制给定的 bean。

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

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

  1. 带有注释的方法 @PostConstruct

  2. afterPropertiesSet()InitializingBean回调接口定义

  3. 自定义配置init()方法

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

  1. 带有注释的方法 @PreDestroy

  2. destroy()DisposableBean回调接口定义

  3. 自定义配置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 在传播一般销毁回调之前首先收到停止通知。但是,在上下文生命周期内的热刷新或停止刷新尝试时,只会调用 destroy 方法。

启动和关闭调用的顺序可能很重要。如果任何两个对象之间存在“依赖”关系,则依赖方在其依赖之后开始,并在其依赖之前停止。然而,有时,直接依赖是未知的。您可能只知道某种类型的对象应该在另一种类型的对象之前开始。在这些情况下,SmartLifecycle接口定义了另一个选项,即getPhase()在其超级接口上定义的方法, Phased。以下清单显示了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表示对象应该最后启动并首先停止(可能是因为它依赖于其他正在运行的进程)。在考虑阶段值时,了解任何Lifecycle未实现的“正常”对象的默认阶段 SmartLifecycle是0. 因此,任何负相位值都表示对象应该在这些标准组件之前开始(并在它们之后停止)。对于任何正相位值,反之亦然。

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


    
    

精彩继续

你可能感兴趣的:(SpringCore笔记(二))