b Spring之scope的介绍

–> go to 总目录


scope,对于bean的创建,有一系列原则。这个原则叫scope。同时在spring中,你不仅可以控制一个bean,还可以用scope来控制一堆bean。最后会教你如何自定义bean

scope描述
singleton默认:单例模式
prototype被引用一次,就创建一个新的
request在http-request生命周期有用。每一个request都有一个该bean的复制。只在web-aware Spring ApplicationContext有用
session只在HTTP Session生命周期里存活一个bean,只在web-aware Spring ApplicationContext有用
application只在一个应用声明周期里有用,只在web-aware Spring ApplicationContext有用
websocket只在WebSocket里有用,只在web-aware Spring ApplicationContext有用

5.1 singleton Scope

一旦声明一个bean为singleton,其他地方的引用都指向同一个实例。默认的模式就是singleton,singleton是先于container和其他bean创建的。
在这里插入图片描述
在这里插入图片描述

5.2 Prototype Scope

每个引用都是一个新的对象
在这里插入图片描述

5.3 singleton Bean引用prototype bean

当一个singleton Bean引用了prototype bean。那么prototype 会在singleton实例化的时候一同实例化。请注意,因为只在一开始就初始画了,所以想在在runtime阶段每次都获取一个新的prototype bean是不可能的。请参考使用method injecton来实现在运行时的注入。

5.4 Request, Session, Application, and WebSocket Scopes

The request, session, application, and websocket 通通都是
web-aware Spring ApplicationContext 的实现 (比如 XmlWebApplicationContext)。如果用在正常的Spring IoC containers, 比如 ClassPathXmlApplicationContext就会抛出IllegalStateException

初始化Web Configuration

为了支持request, session, application, and websocket scopes的初始化,就需要一些最小元素的bean需要被实例化。这些最小元素的初始化并不是singleton和prototype。
如果你在Spring Web MVC框架里获取Scope beans,事实上,在一个请求被Spring 的DispatcherServlet处理之前。没有具体的启动是必要的。因为DispatcherServlet早已经准备好了所有相关联的状态。
如果你使用 Servlet 2.5 Web container,在Spring DispatcherServlet处理request过程之外,你还须要注册org.springframework.web.context.request.RequestContextListener ServletRequestListener。对于For Servlet 3.0+,这一步可以通过使用WebApplicationInitializer。对于更晚的容器,可以这么声明你的容器
在这里插入图片描述
如果你使用listener在启动时报错,你还可以RequestContextFilter,并设置相应的路由
在这里插入图片描述
DispatcherServlet, RequestContextListener,和 RequestContextFilter都干了一件事—绑定了这个HTTP-Request 对象到一个线程上Thread用来处理请求。这样就被 request-scoped和session-scoped捕获,在接下来的call chain(调用链)上。

Request scope

在这里插入图片描述
spring container创建一个LoginAction实例来处理每一个request,通过使用loginAction bean的定义。每一个request来时就创建一个,request过后就消失。
@RequestScope和@Component一起使用等同于xml
在这里插入图片描述

Session scope

在这里插入图片描述
spring 容器会创建一个UserPreferences的bean,关联到每个Http Session。

一个Http Session就是一次user的登录过程。

在这里插入图片描述

Application Scope

在这里插入图片描述
spring 容器会创建一个AppPreferences的bean,关联到ServletContext
ServletContext是个单例,AppPreferences会是其属性。
在这里插入图片描述

Scoped Beans 作为依赖

Spring Ioc conatiner 管理的不仅是实例化你的beans,也可以聚合一堆beans就行包裹。如果你把一个HTTP request-scoped bean注入另一个生命周期更长的scope,你也可以选择注入一个AOP PROXY代替 这个scope bean。也就是说,您需要注入一个代理对象,该对象和scope对象相同的public interface,但也可以从相关scope(例如HTTP请求)中检索实际目标对象,并将方法调用委托给实际对象。

您可以在scope为singleton的bean之间使用<aop:scoped-proxy />,然后引用将通过可序列化的中间代理,因此能够在反序列化时重新获得目标单例bean。
当针对scope为prototype的bean声明**<aop:scoped-proxy />时,共享代理上的每个方法调用都会导致创建新的目标实例,然后将该调用转发到该目标实例。
同样,scope proxies不是以生命周期安全的方式从较短的socpe访问bean的唯一方式。也可以将injection point(即 the constructor 或 setter argument 或 autowired field)声明为ObjectFactory ,从而允许getObject()调用 在需要时返回当前实例–无需单独保留实例的引用。
作为扩展的变体,您可以声明ObjectProvider ,它提供了几个附加的访问变体,包括getIfAvailable和getIfUnique。
JSR-330的这种变体称为Provider,并与Provider 声明和每次检索尝试的相应get()调用一起使用。有关JSR-330总体的更多详细信息,请参见此处。

下面的示例仅仅是一个单线,但是对理解"why"和"how"很有帮助
在这里插入图片描述

定义了这个代理
去创建这样一个代理。你插入了一个子标签
<aop:scoped-proxy/>到一个scoped bean的定义处。为什么要在request、session和自定义的custom-scope级别要求aop:scoped-proxy元素?考虑下列这个singleton bean的定义,并且和上述的示例做比较(注意下面这个userPreferences的定义是不完整的,仅作示范)
在这里插入图片描述
上一个示例中,这个singleton bean(userManager)被注入了HTTP Session-scoped bean(userPerferences)。显著的点是userManager是一个singleton:它仅仅被每个容器实例化一次,并且它的依赖(这个实例中只有一个userPreferencesbean)仅仅被注入一次。这意味着userManagerbean永远只会操作一个相同的userPreference(这是第一次被注入时的bean)。
但是这并不是你想要的行为,当你把一个短存活的scoped bean 注入到长存活的scoped bean (例如把一个 Http Session-scoped bean 当做依赖注入到 singleton bean )。相反,你需要一个单独userManager对象,对于生命周期Http Session,你需要一个和其关联的UserPreferences,而不是定死的。因此,容器做了一个公共的UserPreferences class的interface,当需要UserPreferences对象的时候利用接口来获取。容器呢,就会把proxy对象注入到userManager bean,其并不会关心UserpreFerences是一个代理。在这个例子中,当一个UserManager 实例调用UserPreferences方法,事实上调用的是他的代理。而这个proxy就会真正的fecth
HTTP Session 的UserPreferences。
因此你就需要按以下的格式使用。
在这里插入图片描述

选择代理的方式

默认,spring 容器使用代理会用aop:scoped-proxy/元素–基于CGLIB的类就会被创建。

cglib 只能拦截公共方法,不能拦截非公共方法。他们不能被委托一个实际的 scoped
target 对象。

可选的,你可以定义一个标准的JDK interface-based 代理,通过设置`<aop:scoped-proxy/

`的proxy-target-class为false。意味着你不需要额外的库。然而,这也意味着scoped bean的类必须实现至少一个接口,并且注入scoped bean的所有合作类必须通过其接口之一引用bean。
在这里插入图片描述

5.5 自定义scopes

bean socope 组件是可以扩展的,你可以定义自己的scope或者覆盖存在的socope,但是不能覆盖singletonprototype

创建一个自定义的 Scope

需要继承org.springframework.beans.factory.config.Scope

Scope interface拥有四个方法获取、删除、摧毁 bean

  • Object get(String name, ObjectFactory<?> objectFactory)
    从容器中获取bean对象

  • Object remove(String name)
    删除

  • void registerDestructionCallback(String name, Runnable destructionCallback)
    注册回调函数,当scope被摧毁或者具体的对象被摧毁

  • String getConversationId()
    获取会话标识

使用自定义Scope

  • 注册方法的介绍

自定义scope后需要让Spring 容器识别你的scopes。
在这里插入图片描述
是spring 容器注册scope核心方法,该方法位于ConfigurableBeanFactory接口–通过BeanFactory属性可以获取(被大多数的ApplicationContext)实现。
第一个参数是scope名称,第二个是Scope实例

  • 示例

编程式
在这里插入图片描述
然后用如下形式添加到 thread scope规则里
在这里插入图片描述
声明式
你也不必编程式的注册这个scope的实现,也可以利用CustomScopeConfigurer类去声明式的注册
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值