您的位置:首页 > 编程语言 > Java开发

Spring中Bean的作用域—Spring官方文档系列

2018-04-15 18:17 776 查看
spring bean的作用域分为以下五种:1、singleton(默认模式):单例,指一个bean容器中只存在一份2、prototype:每次请求(每次使用)创建新的实例,destroy方式不生效3、request:每次http请求创建一个实例且仅在当前request内有效4、session:同上,每次http请求创建,当前session中有效5、global session:基于portlet的web中有效(portlet定义了global sessio),如果在web中,同session

一、单例作用域

            单例bean只会产生一个实例,并且所有对具有与该bean定义匹配的id或id的bean的请求Spring容器都只会返回一个实例。换句话说,当你定义一个bean定义,并且作为一个singleton,Spring IoC容器创建该bean所定义的对象的一个实例。 这个单个实例存储在这样的单例bean的缓存中,所有后续的请求和引用返回缓存的这个对象。 Spring的单例bean的概念不同于Singleton模式,和在Gang of Four(GoF)模式的书中定义的不同的地方。 GoF Singleton硬编码对象的范围,使得每一个类加载器内会产生单例类的一个实例。 Spring单例的作用域恰如其名:*一个容器对应一个bean *。 这意味着如果你在一个Spring容器中为一个特定的类定义了一个bean,那么Spring容器将创建一个实例,并且只有一个由该bean定义定义的类的实例。 *singleton scope (单例作用域)*是Spring 中的默认配置。 要将bean定义为XML中的单例,您可以编写,例如:

<bean id="accountService" class="com.foo.DefaultAccountService"/>

<!-- 和下面的写法等价,因为单例作用域是默认的 -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>

    还可以通过注解定义,例如:

  1. import java.util.HashSet;  
  2. import java.util.Set;  
  3.   
  4. import org.springframework.context.annotation.Scope;  
  5. import org.springframework.stereotype.Controller;  
  6. import org.springframework.web.bind.annotation.RequestMapping;  
  7.   
  8. @Controller  
  9. @RequestMapping(value = "/spring")  
  10. @Scope("prototype")  
  11. public class SpringDemo {  
  12.   
  13.     private static Set<Object> set = new HashSet<>();  
  14.   
  15.     @RequestMapping(value="test")  
  16.     public void test(){  
  17.         set.add(this);  
  18.         System.out.println(set.size());  
  19.     }  

二、prototype  作用域

设置bean作用域为prototype,就是非单例,bean部署的prototype scope导致每次对该特定bean的请求时都会创建新的bean实例。 也就是说,bean被注入到另一个bean中,或者通过容器上的

getBean()
方法调用来请求它。 通常,对所有有状态bean使用prototype scope,对无状态bean使用singleton scope。下图说明了Spring prototype scope。 数据访问对象(DAO)通常不被配置为 prototype scope,因为通常DAO不持有任何会话状态;  如何在XML中定义prototype bean:

<
1bb8b
bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>

与其他作用域相比,Spring不管理prototype bean的完整生命周期:容器实例化,配置和以其他方式组装原型对象,并将其传递给客户端,没有原型实例的进一步记录。因此,尽管初始化生命周期回调方法在所有对象上被调用,不管范围如何,在prototype的情况下,配置的销毁生命周期回调被不调用。客户端代码必须清理prototype作用域对象,并释放prototype bean持有的昂贵资源。要使Spring容器释放prototype作用域的bean所拥有的资源,请尝试使用自定义bean post-processor,它持有需要被清理bean的引用。在某些方面,Spring容器在prototype作用域bean中的作用是Java“new”运算符的替代。经由该点的所实例化的bean的所有生命周期管理必须由客户端处理。


Singleton beans 与prototype-bean依赖关系:

    这里简单说一下 Singleton  bean 与 prototype bean 之间的依赖关系 :

    在大多数应用场景中,容器中的大多数bean都是 singletons。 当单例bean需要与另一个单例bean协作或非单例bean需要与另一个非单例bean协作时,通常通过将一个bean定义为另一个的属性来处理依赖关系。不过对于具有不同生命周期的bean 来说这样做就会出现问题。 假设单例bean A需要使用非单例(原型)bean B,也许在A上的每个方法调用上。容器仅创建单例bean A一次,因此只有一次机会来设置属性。 这样就没办法 在需要的时候每次让容器为bean A提供一个新的bean B实例。

解决方案是放弃一些控制的反转。 您可以通过实现以下操作来 定义一个类实现 

ApplicationContextAware
接口,并通过对容器调用getBean(“B”)在每次bean A需要它时调用SpringContextHolder的静态方法getBean()来获取实例B, 如下:
@Service()
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {

private static ApplicationContext applicationContext = null;

/**
* 实现ApplicationContextAware接口, 注入Context到静态变量中.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
if (SpringContextHolder.applicationContext != null) {
System.out.println("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
}
System.out.println("Spring容器启动,将容器实例注入到SpringContextHolder实例bean中");
SpringContextHolder.applicationContext = applicationContext;
}

/**
* 实现DisposableBean接口,重写destroy方法,相当于destroy-method,bean被销毁的时候被调用,
* 实现在Context关闭时清理静态变量的目的
* 令:还有InitializingBean接口,重写afterPropertiesSet方法,相当于init-method,bean被实例化的时候被调用
*/
@Override
public void destroy() throws Exception {
applicationContext = null;
}

/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}

/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}

/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
return applicationContext.getBean(requiredType);
}
}

三、Request, session, application, and WebSocket scopes

    request,session,global session作用域,只有在spring web ApplicationContext的实现中(比如XmlWebApplicationContext)才会起作用,若在常规Spring IoC容器中使用,比如ClassPathXmlApplicationContext中,就会收到一个异常IllegalStateException来告诉你不能识别的bean作用域

Request scope

[p]考虑下面这种bean定义:

<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>
Spring容器通过对每个HTTP请求使用
loginAction
 bean定义来创建一个
LoginAction
 bean的新实例。 也就是说,
loginAction
 bean的作用域是在HTTP请求级别。 您可以根据需要更改创建的实例的内部状态,因为根据此
loginAciton
bean定义创建的其他bean实例并不会看到这些状态的改变;他们为各自的request拥有。 当reqeust完成处理,request作用的bean就被丢弃了。当使用注解驱动组件或Java Config时,
@RequestScope
注解可以用于将一个组件分配给
request
范围。[/p]
@RequestScope
@Component
public class LoginAction {
// ...
}

Session scope

考虑下面这种bean的xml配置定义:

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

Spring容器通过对单个HTTP会话的生命周期使用

userPreferences
 bean定义来创建
UserPreferences
 bean的新实例。 换句话说,
userPreferences
 bean在HTTP
Session
级别有效地作用域。 和
request-scoped
bean相类似,可以改变bean实例的内部状态,不管bean创建了多少实例都可以,要知道,使用相同的
userPreferences
定义创建的其他的bean实例看不到这些状态的改变,因为他们都是为各自的HTTP Session服务的。 当HTTP
Session
最终被丢弃时,被限定为该特定HTTP
Session
的bean也被丢弃。当使用注解驱动组件或Java Config时,
@SessionScope
注解可用于将一个组件分配给
session
范围。

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

Application scope

考虑下面这种bean定义:

<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>

Spring容器通过对整个web应用程序使用

appPreferences
 bean定义来创建一个
AppPreferences
 bean的新实例。 也就是说,
appPreferences
 bean是在
ServletContext
级别定义的,存储为一个常规的
ServletContext
属性。 这有点类似于Spring单例bean,但在两个重要方面有所不同:1、他是每一个
ServeltContext
一个实例,而不是Spring
ApplicationContext
范围。2、它是直接暴露的,作为
ServletContext
属性,因此可见。当使用注解驱动组件或Java Config时,
@ApplicationScope
注解可用于将一个组件分配给
application
作用域。

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

不同级别作用域bean之间依赖

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

你也可以在定义为“singleton”的bean之间使用
<aop:scoped-proxy/>
,然后通过引用一个可序列化的中间代理,因此能够在反序列化时重新获得目标单例bean。
当对
prototype
 scope的bean声明
<aop:scoped-proxy/>
时,共享代理上的每个方法调用都将导致创建一个新的目标实例,然后将该调用转发给该目标实例。
这里对<aop:scoped-proxy/> 不做详细解释 。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- an HTTP Session-scoped bean exposed as a proxy -->
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<!-- instructs the container to proxy the surrounding bean -->
<aop:scoped-proxy/>
</bean>

<!-- a singleton-scoped bean injected with a proxy to the above bean -->
<bean id="userService" class="com.foo.SimpleUserService">
<!-- a reference to the proxied userPreferences bean -->
<property name="userPreferences" ref="userPreferences"/>
</bean>
</beans>


自定义作用域 认为是不推荐的做法,并且,你不能覆盖内置的

singleton
prototype
作用域,有兴趣的同学可以百度。

此文章参考 



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: