ioc-scope
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:它仅仅被每个容器实例化一次,并且它的依赖(这个实例中只有一个userPreferences
bean)仅仅被注入一次。这意味着userManager
bean永远只会操作一个相同的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,但是不能覆盖singleton和prototype
创建一个自定义的 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类去声明式的注册