深入理解Spring4框架(五)——作用域



     我们可以通过使用Bean定义来指定实例的作用域(scope),而不需要在Java的类级别来完成这个任务,这种方法非常强大和灵活。Spring框架现有五种作用域,其中有三个需要在使用Web相关的ApplicationContext环境下才可以使用。

 Spring框架现有的Bean作用域

1singleton:默认的作用域,仅为每个Bean对象创建一个实例。

2prototype:可以根据需要为每个Bean对象创建多个实例。

3request:为每个HTTP请求创建它自有的一个Bean实例,仅在Web相关的ApplicationContext中生效。

4session:为每个HTTP会话创建一个实例,仅在Web相关的ApplicationContext中生效。

5global session:为每个全局的HTTP会话创建一个实例。一般仅在porlet上下文中使用生效。同时仅在Web相关的ApplicationContext中生效。

6application:为每个ServletContext创建一个实例。仅在Web相关的ApplicationContext中生效。

1 singleton作用域

容器仅维持着Bean的一个共享的实例,每次通过id匹配到这个Bean定义时,容器仅返回一个指定的Bean实例。

换句话说,当将一个Bean的作用域设置为singleton时,SpringIoC容器仅仅根据Bean定义创建一个实例(简称单例)。单例被缓存起来了,对这个单例的所有引用都会返回缓存中的数据。

Spring中所说的单例BeanGang of Four(GoF)设计模式书中所描述的单例模式是不同的。GoF描述的是,指定的类在每个ClassLoader中仅会创建一个实例,并且是通过硬编码来完成的。而Spring中的单例最好解释为每个容器仅有Bean的一个实例。这就意味着,如果在Spring容器中定义一个Bean,容器将会根据定义为Bean创建仅仅一个实例。在Spring中,singleton作用域是默认的作用域。

2 prototype作用域

非单例的prototype作用域,在每次请求一个指定Bean的时候都会创建一个新的实例。通常,对于具有状态的Bean使用prototype,对于无状态的Bean使用singleton

下面的例子将一个Bean定义为prototype

与其它作用域相比,Spring不会管理一个prototype作用域Bean的完整生命周期。容器实例化、配置和组装一个prototype实例,然后转交给客户端,不再继续跟踪那个prototype实例。因此,处于初始化生命周期的回调方法在所有作用域下都会被调用,而在prototype作用域下,析构生命周期回调不会被调用。客户端代码必须清理prototype作用域对象,释放对象所持有的昂贵的资源。

当单例Bean依赖prototype类型的Bean时,注意这种依赖是在实例化的时候解析的,一个新的prototype类型的Bean会被实例化然后注入到单例Bean中,因此这个prototype类型的Bean对于这个单例Bean而言只有一个,而不是我们所认为的为每次请求单例Bean会创建一个新的prototype类型的Bean。如果要为每次请求单例Bean创建一个新的prototype类型的Bean,就不能这样做了,因为注入只会发生一次,即在Spring容器在实例化单例和解析依赖的时候。若需要这样的功能,可以参考方法注入

3 request, session, global sessionapplication作用域

request, sessionglobal session作用域仅在使用Web相关的ApplicationContext实现(比如XmlWebApplicationContext)时才会有用。如果使用平常的Spring IoC容器(比如ClassPathXmlApplicationContext),将会抛出IllegalStateException异常。

为了支持request, sessionglobal session作用域,在定义Bean之前需要做一些必要的配置。具体如何完成这样的配置决定于特定的Servlet环境。如果使用的是Spring Web MVC,通过DispatcherServlet或者DispatcherPortlet来执行请求,不需要做任何设置,DispatcherServlet或者DispatcherPortlet已经暴露了所有相关的状态。

如果使用的是Servlet2.5Web容器,且使用的是DispatcherServlet之外的Servlet(比如Struts)来执行请求,那么就需要注册org.springframework.web.context.request.RequestContextListener ServletRequestListener。对于Servlet3.0+,可以通过WebApplicationInitializer接口编码实现这样的功能。

3.1 request作用域

请看如下示例:

Spring容器为每个HTTP请求创建一个新的loginAction实例,这个实例的状态可以根据需要进行修改,而不会影响到其它请求中创建的实例。当请求执行完毕时,那么这个实例就会被遗弃。

3.2 session作用域

请看如下示例:

Spring容器会为每个会话创建一个userPreferences实例。跟request作用域类似,实例的状态可以根据需要进行修改,不会影响到其它会话中创建的实例。

3.3 global session作用域

global session作用域跟session作用域类似,仅应用于基于portlet的上下文。

3.4 application作用域

请看如下示例:

Spring会为整个Web应用创建一个appPreferences实例,作为ServletContext的一个属性存储着。它跟Spring的单例Bean有点像,但也有不同,它在每个ServletContext中是单例的,而非SpringApplicationContext

3.5 作用域Bean依赖

如果想把一个HTTP request作用域的Bean注入到另一个长周期作用域的Bean中,就需要选择注入一个AOP代理来替换这个request作用域Bean。也就是,需要注入一个代理对象,这个代理对象暴露了request作用域Bean的公共接口,可以查找到真实的目标对象,然后将方法调用委托到真实的对象上面。比如在单例Bean中注入一个会话Bean,若不使用代理,那么在会话Bean失效之后,单例Bean将不能再获取到会话Bean。若使用代理,那么代理对象将从作用域机制中找到对应的Bean

示例如下:



    
    
        
        
    
    
    
        
        
    

你可能感兴趣的:(Spring)