如何使用spring的作用域:
<beanid="role" class="spring.chapter2.maryGame.Role"scope="singleton"/>
这里的scope就是用来配置spring bean的作用域,它标识bean的作用域。在spring2.0之前bean只有2种作用域即:singleton(单例)、non-singleton(也称prototype), Spring2.0以后,增加了session、request、globalsession三种专用于Web应用程序上下文的Bean。因此,默认情况下Spring2.0现在有五种类型的Bean。当然,Spring2.0对Bean的类型的设计进行了重构,并设计出灵活的Bean类型支持,理论上可以有无数多种类型的Bean,用户可以根据自己的需要,增加新的Bean类型,满足实际应用需求
Spring-mvc 3.0 application session scope
1、singleton作用域
当一个bean的作用域设置为singleton, 那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。换言之,当把一个bean定义设置为singleton作用域时,Spring IOC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都将返回被缓存的对象实例,这里要注意的是singleton作用域和GOF设计模式中的单例是完全不同的,单例设计模式表示一个ClassLoader中只有一个class存在,而这里的singleton则表示一个容器对应一个bean,也就是说当一个bean被标识为singleton时候,spring的IOC容器中只会存在一个该bean。
配置实例:
<beanid="role" class="spring.chapter2.maryGame.Role"scope="singleton"/>
或者
<beanid="role" class="spring.chapter2.maryGame.Role"singleton="true"/>
2、prototype
prototype作用域部署的bean,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)都会产生一个新的bean实例,相当与一个new的操作,对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被singleton作用域bean占用资源的一种可行方式是,通过使用bean的后置处理器,该处理器持有要被清除的bean的引用。)
配置实例:
<bean id="role"class="spring.chapter2.maryGame.Role"scope="prototype"/>
或者
<beanid="role"class="spring.chapter2.maryGame.Role"singleton="false"/>
3、request
request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,配置实例: request、session、globalsession使用的时候首先要在初始化web的web.xml中做如下配置:如果你使用的是Servlet 2.4及以上的web容器,那么你仅需要在web应用的XML声明文件web.xml中增加下述ContextListener即可:
<web-app>
...
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
...
</web-app>
如果是Servlet2.4以前的web容器,那么你要使用一个javax.servlet.Filter的实现:
<web-app>
..
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
</web-app>
接着既可以配置bean的作用域了:
<beanid="role" class="spring.chapter2.maryGame.Role"scope="request"/>
4、session
session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTPsession内有效,配置实例:
配置实例:和request配置实例的前提一样,配置好web启动文件就可以如下配置:
<bean id="role"class="spring.chapter2.maryGame.Role" scope="session"/>
5、global session
global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。如果你在web中使用global session作用域来标识bean,那么web会自动当成session类型来使用。配置实例:和request配置实例的前提一样,配置好web启动文件就可以如下配置:
<bean id="role" class="spring.chapter2.maryGame.Role"scope="global session"/>
6、自定义bean装配作用域在spring2.0中作用域是可以任意扩展的,你可以自定义作用域,甚至你也可以重新定义已有的作用域(但是你不能覆盖singleton和prototype),spring的作用域由接口org.springframework.beans.factory.config.Scope来定义,自定义自己的作用域只要实现该接口即可,下面给个实例:我们建立一个线程的scope,该scope在表示一个线程中有效,代码如下:
publicclass MyScope implements Scope ...{
privatefinal ThreadLocal threadScope = new ThreadLocal() ...{
protected Object initialValue() ...{
returnnew HashMap();
}
};
public Object get(String name, ObjectFactory objectFactory) ...{
Map scope = (Map) threadScope.get();
Object object = scope.get(name);
if(object==null) ...{
object = objectFactory.getObject();
scope.put(name, object);
}
return object;
}
public Object remove(String name) ...{
Map scope = (Map) threadScope.get();
return scope.remove(name);
}
publicvoid registerDestructionCallback(String name, Runnablecallback) ...{
}
public String getConversationId() ...{
// TODO Auto-generated method stub
returnnull;
}
}
/*Scope.java*/
package org.jgroups.protocols;
import org.jgroups.*;
import org.jgroups.util.*;
import org.jgroups.util.ThreadFactory;
import org.jgroups.annotations.*;
import org.jgroups.stack.Protocol;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.Queue;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
/***
* Implements https://jira.jboss.org/jira/browse/JGRP-822, which allows for concurrent delivery of messages from the
* same sender based on scopes. Similar to using OOB messages, but messages within the same scope are ordered.
* @author Bela Ban
* @version $Id: SCOPE.java,v 1.16 2010/09/17 12:03:35 belaban Exp $
* @since 2.10
*/
@Experimental
@MBean(description="Implementation of scopes (concurrent delivery of messages from the same sender)")
public class SCOPE extends Protocol {
protected int thread_pool_min_threads=2;
protected int thread_pool_max_threads=10;
protected long thread_pool_keep_alive_time=30000;
@Property(description="Thread naming pattern for threads in this channel. Default is cl")
protected String thread_naming_pattern="cl";
@Property(description="Time in milliseconds after which an expired scope will get removed. An expired scope is one " +
"to which no messages have been added in max_expiration_time milliseconds. 0 never expires scopes")
protected long expiration_time=30000;
@Property(description="Interval in milliseconds at which the expiry task tries to remove expired scopes")
protected long expiration_interval=60000;
protected Future<?> expiry_task=null;
/***
* Used to find the correct AckReceiverWindow on message reception and deliver it in the right order
*/
protected final ConcurrentMap<Address,ConcurrentMap<Short,MessageQueue>> queues=Util.createConcurrentMap();
protected String cluster_name;
protected Address local_addr;
protected Executor thread_pool;
protected ThreadGroup thread_group;
protected ThreadFactory thread_factory;
protected TimeScheduler timer;
public SCOPE() {
}
@ManagedAttribute(description="Number of scopes in queues")
public int getNumberOfReceiverScopes() {
int retval=0;
for(ConcurrentMap<Short,MessageQueue> map: queues.values())
retval+=map.keySet().size();
return retval;
}
@ManagedAttribute(description="Total number of messages in all queues")
public int getNumberOfMessages() {
int retval=0;
for(ConcurrentMap<Short,MessageQueue> map: queues.values()) {
for(MessageQueue queue: map.values())
retval+=queue.size();
}
return retval;
}
@Property(name="thread_pool.min_threads",description="Minimum thread pool size for the regular thread pool")
public void setThreadPoolMinThreads(int size) {
thread_pool_min_threads=size;
if(thread_pool instanceof ThreadPoolExecutor)
((ThreadPoolExecutor)thread_pool).setCorePoolSize(size);
}
public int getThreadPoolMinThreads() {return thread_pool_min_threads;}
@Property(name="thread_pool.max_threads",description="Maximum thread pool size for the regular thread pool")
public void setThreadPoolMaxThreads(int size) {
thread_pool_max_threads=size;
if(thread_pool instanceof ThreadPoolExecutor)
((ThreadPoolExecutor)thread_pool).setMaximumPoolSize(size);
}
public int getThreadPoolMaxThreads() {return thread_pool_max_threads;}
@Property(name="thread_pool.keep_alive_time",description="Timeout in milliseconds to remove idle thread from regular pool")
public void setThreadPoolKeepAliveTime(long time) {
thread_pool_keep_alive_time=time;
if(thread_pool instanceof ThreadPoolExecutor)
((ThreadPoolExecutor)thread_pool).setKeepAliveTime(time, TimeUnit.MILLISECONDS);
}
public long getThreadPoolKeepAliveTime() {return thread_pool_keep_alive_time;}
@Experimental
@ManagedOperation(description="Removes all queues and scopes - only used for testing, might get removed any time !")
public void removeAllQueues() {
queues.clear();
}
/***
* Multicasts an EXPIRE message to all members, and - on reception - each member removes the scope locally
* @param scope
*/
@ManagedOperation(description="Expires the given scope around the cluster")
public void expire(short scope) {
ScopeHeader hdr=ScopeHeader.createExpireHeader(scope);
Message expiry_msg=new Message();
expiry_msg.putHeader(Global.SCOPE_ID, hdr);
expiry_msg.setFlag(Message.SCOPED);
down_prot.down(new Event(Event.MSG, expiry_msg));
}
public void removeScope(Address member, short scope) {
if(member == null) return;
ConcurrentMap<Short, MessageQueue> val=queues.get(member);
if(val != null) {
MessageQueue queue=val.remove(scope);
if(queue != null)
queue.clear();
}
}
@ManagedOperation(description="Dumps all scopes associated with members")
public String dumpScopes() {
StringBuilder sb=new StringBuilder();
for(Map.Entry<Address,ConcurrentMap<Short,MessageQueue>> entry: queues.entrySet()) {
sb.append(entry.getKey()).append(": ").append(new TreeSet<Short>(entry.getValue().keySet())).append("\n");
}
return sb.toString();
}
@ManagedAttribute(description="Number of active thread in the pool")
public int getNumActiveThreads() {
if(thread_pool instanceof ThreadPoolExecutor)
return ((ThreadPoolExecutor)thread_pool).getActiveCount();
return 0;
}
public void init() throws Exception {
super.init();
timer=getTransport().getTimer();
thread_group=new ThreadGroup(getTransport().getPoolThreadGroup(), "SCOPE Threads");
thread_factory=new DefaultThreadFactory(thread_group, "SCOPE", false, true);
setInAllThreadFactories(cluster_name, local_addr, thread_naming_pattern);
// sanity check for expiration
if((expiration_interval > 0 && expiration_time <= 0) || (expiration_interval <= 0 && expiration_time > 0))
throw new IllegalArgumentException("expiration_interval (" + expiration_interval + ") and expiration_time (" +
expiration_time + ") don't match");
}
public void start() throws Exception {
super.start();
thread_pool=createThreadPool(thread_pool_min_threads, thread_pool_max_threads,
thread_pool_keep_alive_time, thread_factory);
if(expiration_interval > 0 && expiration_time > 0)
startExpiryTask();
}
public void stop() {
super.stop();
stopExpiryTask();
shutdownThreadPool(thread_pool);
for(ConcurrentMap<Short,MessageQueue> map: queues.values()) {
for(MessageQueue queue: map.values())
queue.release(); // to prevent a thread killed on shutdown() from holding on to the lock
}
}
public Object down(Event evt) {
switch(evt.getType()) {
case Event.SET_LOCAL_ADDRESS:
local_addr=(Address)evt.getArg();
break;
case Event.VIEW_CHANGE:
handleView((View)evt.getArg());
break;
case Event.CONNECT:
case Event.CONNECT_WITH_STATE_TRANSFER:
case Event.CONNECT_USE_FLUSH:
case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH:
cluster_name=(String)evt.getArg();
setInAllThreadFactories(cluster_name, local_addr, thread_naming_pattern);
break;
}
return down_prot.down(evt);
}
public Object up(Event evt) {
switch(evt.getType()) {
case Event.MSG:
Message msg=(Message)evt.getArg();
// we don't handle unscoped or OOB messages
if(!msg.isFlagSet(Message.SCOPED) || msg.isFlagSet(Message.OOB))
break;
ScopeHeader hdr=(ScopeHeader)msg.getHeader(id);
if(hdr == null)
throw new IllegalStateException("message doesn't have a ScopeHeader attached");
if(hdr.type == ScopeHeader.EXPIRE) {
removeScope(msg.getSrc(), hdr.scope);
return null;
}
MessageQueue queue=getOrCreateQueue(msg.getSrc(), hdr.scope);
queue.add(msg);
if(!queue.acquire())
return null;
QueueThread thread=new QueueThread(queue);
thread_pool.execute(thread);
return null;
case Event.VIEW_CHANGE:
handleView((View)evt.getArg());
break;
}
return up_prot.up(evt);
}
protected MessageQueue getOrCreateQueue(Address sender, short scope) {
ConcurrentMap<Short,MessageQueue> val=queues.get(sender);
if(val == null) {
val=Util.createConcurrentMap();
ConcurrentMap<Short,MessageQueue> tmp=queues.putIfAbsent(sender, val);
if(tmp != null)
val=tmp;
}
MessageQueue queue=val.get(scope);
if(queue == null) {
queue=new MessageQueue();
MessageQueue old=val.putIfAbsent(scope, queue);
if(old != null)
queue=old;
}
return queue;
}
protected synchronized void startExpiryTask() {
if(expiry_task == null || expiry_task.isDone())
expiry_task=timer.scheduleWithFixedDelay(new ExpiryTask(), expiration_interval, expiration_interval, TimeUnit.MILLISECONDS);
}
protected synchronized void stopExpiryTask() {
if(expiry_task != null) {
expiry_task.cancel(true);
expiry_task=null;
}
}
protected static ExecutorService createThreadPool(int min_threads, int max_threads, long keep_alive_time,
final org.jgroups.util.ThreadFactory factory) {
ThreadPoolExecutor pool=new ThreadManagerThreadPoolExecutor(min_threads, max_threads, keep_alive_time,
TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>());
pool.setThreadFactory(factory);
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return pool;
}
protected static void shutdownThreadPool(Executor thread_pool) {
if(thread_pool instanceof ExecutorService) {
ExecutorService service=(ExecutorService)thread_pool;
service.shutdownNow();
try {
service.awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS);
}
catch(InterruptedException e) {
}
}
}
private void setInAllThreadFactories(String cluster_name, Address local_address, String pattern) {
ThreadFactory[] factories= {thread_factory};
for(ThreadFactory factory:factories) {
if(pattern != null)
factory.setPattern(pattern);
if(cluster_name != null)
factory.setClusterName(cluster_name);
if(local_address != null)
factory.setAddress(local_address.toString());
}
}
private void handleView(View view) {
Vector<Address> members=view.getMembers();
// Remove all non members from receiver_table
Set<Address> keys=new HashSet<Address>(queues.keySet());
keys.removeAll(members);
for(Address key: keys)
clearQueue(key);
}
public void clearQueue(Address member) {
ConcurrentMap<Short,MessageQueue> val=queues.remove(member);
if(val != null) {
Collection<MessageQueue> values=val.values();
for(MessageQueue queue: values)
queue.clear();
}
if(log.isTraceEnabled())
log.trace("removed " + member + " from receiver_table");
}
protected static class MessageQueue {
private final Queue<Message> queue=new ConcurrentLinkedQueue<Message>();
private final AtomicBoolean processing=new AtomicBoolean(false);
private long last_update=System.currentTimeMillis();
public boolean acquire() {
return processing.compareAndSet(false, true);
}
public boolean release() {
boolean result=processing.compareAndSet(true, false);
if(result)
last_update=System.currentTimeMillis();
return result;
}
public void add(Message msg) {
queue.add(msg);
}
public Message remove() {
return queue.poll();
}
public void clear() {
queue.clear();
}
public int size() {
return queue.size();
}
public long getLastUpdate() {
return last_update;
}
}
protected class QueueThread implements Runnable {
protected final MessageQueue queue;
protected boolean first=true;
public QueueThread(MessageQueue queue) {
this.queue=queue;
}
/*** Try to remove as many messages as possible from the queue and pass them up. The outer and inner loop and
* the size() check at the end prevent the following scenario:
* <pre>
* - Threads T1 and T2
* - T1 has the CAS
* - T1: remove() == null
* - T2: add()
* - T2: attempt to set the CAS: false, return
* - T1: set the CAS to false, return
* ==> Result: we have a message in the queue that nobody takes care of !
* </pre>
* <p/>
*/
public void run() {
while(true) { // outer loop
if(first) // we already have the queue CAS acquired
first=false;
else {
if(!queue.acquire())
return;
}
try {
Message msg_to_deliver;
while((msg_to_deliver=queue.remove()) != null) { // inner loop
try {
up_prot.up(new Event(Event.MSG, msg_to_deliver));
}
catch(Throwable t) {
log.error("couldn't deliver message " + msg_to_deliver, t);
}
}
}
finally {
queue.release();
}
// although ConcurrentLinkedQueue.size() iterates through the list, this is not costly,
// as at this point, the queue is almost always empty, or has only a few elements
if(queue.size() == 0) // prevents a concurrent add() (which returned) to leave a dangling message in the queue
break;
}
}
}
protected class ExpiryTask implements Runnable {
public void run() {
try {
_run();
}
catch(Throwable t) {
log.error("failed expiring old scopes", t);
}
}
protected void _run() {
long current_time=System.currentTimeMillis();
for(Map.Entry<Address,ConcurrentMap<Short,MessageQueue>> entry: queues.entrySet()) {
ConcurrentMap<Short,MessageQueue> map=entry.getValue();
for(Iterator<Map.Entry<Short,MessageQueue>> it=map.entrySet().iterator(); it.hasNext();) {
Map.Entry<Short,MessageQueue> entry2=it.next();
Short scope=entry2.getKey();
MessageQueue queue=entry2.getValue();
long diff=current_time - queue.getLastUpdate();
if(diff >= expiration_time && queue.size() == 0) {
it.remove();
if(log.isTraceEnabled())
log.trace("expired scope " + entry.getKey() + "::" + scope + " (" + diff + " ms old)");
}
}
}
}
}
public static class ScopeHeader extends Header {
public static final byte MSG = 1;
public static final byte EXPIRE = 2;
byte type;
short scope=0; // starts with 1
public static ScopeHeader createMessageHeader(short scope) {
return new ScopeHeader(MSG, scope);
}
public static ScopeHeader createExpireHeader(short scope) {
return new ScopeHeader(EXPIRE, scope);
}
public ScopeHeader() {
}
private ScopeHeader(byte type, short scope) {
this.type=type;
this.scope=scope;
}
public short getScope() {
return scope;
}
public int size() {
switch(type) {
case MSG:
case EXPIRE:
return Global.BYTE_SIZE + Global.SHORT_SIZE;
default:
throw new IllegalStateException("type has to be MSG or EXPIRE");
}
}
public void writeTo(DataOutputStream out) throws IOException {
out.writeByte(type);
switch(type) {
case MSG:
case EXPIRE:
out.writeShort(scope);
break;
default:
throw new IllegalStateException("type has to be MSG or EXPIRE");
}
}
public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException {
type=in.readByte();
switch(type) {
case MSG:
case EXPIRE:
scope=in.readShort();
break;
default:
throw new IllegalStateException("type has to be MSG or EXPIRE");
}
}
public String toString() {
StringBuilder sb=new StringBuilder(typeToString(type));
switch(type) {
case MSG:
case EXPIRE:
sb.append(": scope=").append(scope);
break;
default:
sb.append("n/a");
}
return sb.toString();
}
public static String typeToString(byte type) {
switch(type) {
case MSG: return "MSG";
case EXPIRE: return "EXPIRE";
default: return "n/a";
}
}
}
}