项目实训 11 学习spring(5)

230 阅读7分钟

1.5:bean范围:

不仅可以控制要插入到从特定 bean 定义创建的对象中的各种依赖项和配置值,还可以控制从特定 bean 定义创建的对象的范围。

bean的作用域(Scope的值):

image.png

image.png

1.5.1:单例范围:

仅存在一个 singleton bean 的一个共享实例,并且所有对具有 ID 或与该 bean 定义相匹配的 ID 的 bean 的请求都会导致该特定的 bean 实例由 Spring 容器返回。

当您定义一个 bean 定义并且其作用域为单例时,Spring IoC 容器将为该 bean 定义所定义的对象创建一个实例。该单个实例存储在此类单例 bean 的高速缓存中,并且对该命名 bean 的所有后续请求和引用都返回该高速缓存的对象。

1.5.2:原型范围:

每次对特定 bean 提出请求时,bean 部署的非单一原型范围都会导致创建一个新 bean 实例。也就是说,将 Bean 注入到另一个 Bean 中,或者您可以通过容器上的getBean()方法调用来请求它。

image.png

与其他作用域相反,Spring 不管理原型 Bean 的完整生命周期。容器将实例化,配置或组装原型对象,然后将其交给 Client 端,而无需对该原型实例的进一步记录。Client 端代码必须清除原型作用域对象,并释放原型 Bean 拥有的昂贵资源。

1.5.3:具有原型bean依赖关系的单例bean:

如果将依赖项原型的 bean 注入到单例范围的 bean 中,则将实例化新的原型 bean,然后将依赖项注入到单例 bean 中。

1.5.4:请求(request)、会话(session)、应用程序(application)和WebSocket范围:

request,session,application和websocket范围仅在使用 Web 感知的 Spring ApplicationContext实现(例如XmlWebApplicationContext)时可用。如果将这些作用域与常规的 Spring IoC 容器(例如ClassPathXmlApplicationContext)一起使用,则会引发未知 bean 作用域的IllegalStateException。

初始Web配置:

为了支持request,session,application和websocket级别(网络范围的 Bean)的 Bean 范围界定,在定义 Bean 之前,需要一些较小的初始配置。 (对于标准范围singleton和prototype,不需要此初始设置.)

image.png

image.png

Request scope:

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

image.png

Spring 容器通过为每个 HTTP 请求使用loginAction bean 定义来创建LoginAction bean 的新实例。也就是说,loginAction bean 的作用域是 HTTP 请求级别。您可以根据需要更改创建实例的内部状态,因为从同一loginAction bean 定义创建的其他实例看不到这些状态更改。

使用注解驱动的组件或 Java 配置时,可以使用@RequestScope注解 将组件分配给request范围。

image.png

Session Scope:

在单个 HTTP Session的生存期内,Spring 容器通过使用userPreferences bean 定义来创建UserPreferences bean 的新实例。换句话说,userPreferences bean 的作用域实际上是 HTTP Session级别。与请求范围的 Bean 一样,您可以根据需要任意更改所创建实例的内部状态。

使用注解驱动的组件或 Java 配置时,可以使用@SessionScope注解 将组件分配给session范围。

image.png

Application Scope:

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

使用注解驱动的组件或 Java 配置时,可以使用@ApplicationScope注解 将组件分配给application范围。

不同scope的bean作为依赖项:

如果要将(例如)HTTP 请求范围的 Bean 注入(例如)另一个作用域更长的 Bean,则可以选择注入 AOP 代理来代替已定义范围的 Bean。也就是说,您需要注入一个代理对象,该对象公开与范围对象相同的公共接口,但也可以从相关范围(例如 HTTP 请求)中检索实际目标对象,并将方法调用委托给该真实对象。

可以在范围为singleton的 bean 之间使用aop:scoped-proxy/,然后引用将通过可序列化的中间代理,因此可以在反序列化时重新获得目标单例 bean。

当针对范围为prototype的 bean 声明aop:scoped-proxy/时,共享代理上的每个方法调用都会导致创建新的目标实例,然后将该调用转发到该目标实例。

例:

image.png

为什么需要aop:scoped-proxy元素:

由于单例 bean(userManager)注入了对 HTTP Session范围的 bean(userPreferences)的引用。这里的重点是userManager bean 是单例的:每个容器仅实例化一次,并且它的依赖项(在这种情况下,仅一个userPreferences bean)也仅注入一次。这意味着userManager bean 仅在完全相同的userPreferences对象(即最初与之注入对象)上操作。

故需要的是一个userManager对象,并且在 HTTP Session的生存期内,您需要一个特定于 HTTP Session的userPreferences对象。

所以采用aop:scoped-proxy元素时,容器创建了一个对象,该对象公开了与UserPreferences类完全相同的公共接口(理想情况下是UserPreferences实例的对象),该对象可以从范围机制(HTTP 请求,Session等)中获取实际的UserPreferences对象。容器将此代理对象注入到userManager bean 中,而后者不知道此UserPreferences引用是代理。故,当UserManager实例在注入依赖项的UserPreferences对象上调用方法时,实际上是在代理上调用方法。然后,代理从 HTTP Session(在这种情况下)获取真实的UserPreferences对象,并将方法调用委托给检索到的真实的UserPreferences对象。

选择要创建的代理类型:
默认情况下,当 Spring 容器为使用aop:scoped-proxy/元素标记的 bean 创建代理时,将创建基于 CGLIB 的类代理。CGLIB 代理仅拦截公共方法调用!不要在此类代理上调用非公共方法。

您可以通过将aop:scoped-proxy/元素的proxy-target-class属性的值指定为false来配置 Spring 容器为此类作用域的 bean 创建基于标准 JDK 接口的代理。意味着作用域 bean 的类必须实现至少一个接口,并且作用域 bean 注入到的bean都必须通过其接口之一引用该 bean。

例:

image.png

1.5.5:自定义范围:

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

创建自定义范围:

要将自定义范围集成到 Spring 容器中,您需要实现org.springframework.beans.factory.config.Scope接口。

Scope接口有四种方法可以从范围中获取对象,从范围中删除对象,然后销毁它们。

从基础范围返回对象:

       Object get(String name, ObjectFactory objectFactory)

从基础范围中删除该对象:

       Object remove(String name)

以下方法注册在销毁作用域或销毁作用域中的指定对象时作用域应执行的回调:

       void registerDestructionCallback(String name, Runnable destructionCallback)

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

       String getConversationId()

使用自定义范围:

在编写并测试一个或多个自定义Scope实现之后,您需要使 Spring 容器意识到您的新作用域。

在 Spring 容器中注册新的Scope的中心方法:

       void registerScope(String scopeName, Scope scope);

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

例:

image.png

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

image.png