您当前的位置: 首页 > 

wespten

暂无认证

  • 2浏览

    0关注

    899博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

ThreadLocal来存储Session,以便实现Session any where

wespten 发布时间:2018-12-10 22:30:23 ,浏览量:2

1.Application对象 

    多个用户共享的应用级别的作用域,在服务器端,相比前两者,这个存在时间是最长的,只有当关闭服务器的时候才死亡!所以他可以活很长时间。     Application用于保存所有用户的公共的数据信息,如果使用Application对象,一个需要考虑的问题是任何写操作都要在Application_OnStart事件(global.asax)中完成.尽管使用Application.Lock和Applicaiton.Unlock方法来避免写操作的同步,但是它串行化了对Application对象的请求,当网站访问量大的时候会产生严重的性能瓶颈.因此最好不要用此对象保存大的数据集合

2.Session对象

    session是服务器端技术,利用这个技术,服务器可以把与会话相关的数据写到一个代表会话的 session对象中,用来存储用户跨网页程序的变量或对象,只针对单一用户。     Session用于保存每个用户的专用信息.她的生存期是用户持续请求时间再加上一段时间(一般是20分钟左右).Session中的信息保存在Web服务器内容中,保存的数据量可大可小.当Session超时或被关闭时将自动释放保存的数据信息.由于用户停止使用应用程序后它仍然在内存中保持一段时间,因此使用Session对象使保存用户数据的方法效率很低.对于小量的数据,使用Session对象保存还是一个不错的选择.使用Session对象保存信息: session有效期可以自己设置 方法一:在web.xm中使用l的子标签 ,单位为分钟,主要是针对整个应用的所有session。 方法二: 

    HttpSession session  = request.getSession();
    session.setMaxInactiveInterval(“自己想要设置的具体时间”)。

默认情况下关闭浏览器session就失效,但是可以手动设置时间的。

3.Cookie对象     Cookie用于保存客户浏览器请求服务器页面的请求信息,程序员也可以用它存放非敏感性的用户信息,信息保存的时间可以根据需要设置.如果没有设置Cookie失效日期,它们仅保存到关闭浏览器程序为止.如果将Cookie对象的Expires属性设置为Minvalue,则表示Cookie永远不会过期.Cookie存储的数据量很受限制,大多数浏览器支持最大容量为4096,因此不要用来保存数据集及其他大量数据.由于并非所有的浏览器都支持Cookie,并且数据信息是以明文文本的形式保存在客户端的计算机中,因此最好不要保存敏感的,未加密的数据,否则会影响网站的安全性.使用Cookie对象保存的代码如下:

//存放信息
Response.Cookies["UserID"].Value="0001";
//读取信息
string UserID=Response.Cookies["UserID"].Value;
Cookie cookie = new Cookie(“mycookie”,“name”);
cookie.setMaxAge("自己指定的时间")。。

 cookie存放在客户端中,因此有效期时间以客户端的时间为准。可以自己手动设置,  如果没有指定Cookies对象的有效期,则Cookies对象只存在于客户端的内存。当浏览器关闭时,Cookies就会失效。

ThreadLocal来存储Session,以便实现Session any where

ThreadLocal使用场景用来解决 数据库连接、Session管理等。

package com.enation.framework.context.webcontext;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.log4j.Logger;
 
import com.enation.framework.context.webcontext.impl.WebSessionContextImpl;
 
 
/**
 *  用ThreadLocal来存储Session,以便实现Session any where 
 * @author kingapex
 * 

2009-12-17 下午03:10:09

 * @version 1.1  * 新增request any where  */ public class ThreadContextHolder  {   protected static final Logger logger = Logger.getLogger(ThreadContextHolder.class);   private static ThreadLocal SessionContextThreadLocalHolder = new ThreadLocal(); private static ThreadLocal HttpRequestThreadLocalHolder = new ThreadLocal(); private static ThreadLocal HttpResponseThreadLocalHolder = new ThreadLocal();     public static void setHttpRequest(HttpServletRequest request){     HttpRequestThreadLocalHolder.set(request);   }   public static HttpServletRequest getHttpRequest(){   return  HttpRequestThreadLocalHolder.get();   }       public static void remove(){   SessionContextThreadLocalHolder.remove(); HttpRequestThreadLocalHolder.remove(); HttpResponseThreadLocalHolder.remove();   }     public static void setHttpResponse(HttpServletResponse response){   HttpResponseThreadLocalHolder.set(response);   }   public static HttpServletResponse getHttpResponse(){     return HttpResponseThreadLocalHolder.get();   }       public static void setSessionContext(WebSessionContext context) {   SessionContextThreadLocalHolder.set(context);   }   public static void destorySessionContext() {   WebSessionContext context = SessionContextThreadLocalHolder.get(); if (context != null) {   context.destory();   }   }   public static   WebSessionContext  getSessionContext() {   if (SessionContextThreadLocalHolder.get() == null) {   //if(logger.isDebugEnabled()) //logger.debug("create new webSessionContext."); SessionContextThreadLocalHolder.set(new WebSessionContextImpl());   }else{   //if(logger.isDebugEnabled()) //logger.debug(" webSessionContext not null and return ...");   } return SessionContextThreadLocalHolder.get();   }     }

ThreadLocal获取session

ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。

ThreadLocal在每个线程中对该变量会创建一个副本,即每个线程内部都会有一个该变量,且在线程内部任何地方都可以使用,线程之间互不影响,这样一来就不存在线程安全问题,也不会严重影响程序执行性能。

但是要注意,虽然ThreadLocal能够解决上面说的问题,但是由于在每个线程中都创建了副本,所以要考虑它对资源的消耗,比如内存的占用会比不使用ThreadLocal要大。

ThreadLocal类提供的几个方法:

1、ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值,获取ThreadLocal在当前线程中保存的变量副本

2、ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值,设置当前线程中变量的副本

3、ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。

4、ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值,是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法

    一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程。

   也就是说,同一线程贯通N层,不同的线程可能由于参数等不同会对程序中的某些变量进行修改,但是又要防止修改后的值对其它线程产生影响,因为不同的线程可以同时运行滴,这就需要我们解决对某些线程共享的变量的访问冲突问题。ThreadLocal本地线程变量就是一种解决方式,它通过将程序中不安全的变量封装进ThreadLocal中,这相当于为每一个线程提供一个独立的变量副本(其实是不同的对象),线程修改变量的值对其它线程来说没影响了,因为其它线程有自己的一个副本信息。

二、借助ThreadLocal对象每个线程只创建一个实例

 public static final String dateFormat="yyyy-MM-dd";
 private static final ThreadLocal dfThreadLocal=new ThreadLocal(){
      @Override
      protected DateFormat initialValue() {
          return new SimpleDateFormat(dateFormat);
      }
 };
 public static String dateToString(Date date){
      return dfThreadLocal.get().format(date);
}

对于每个线程,都有一个类似于Map的东西ThreadLocalMap(ThreadLocal的静态类 ),那它里面保存了什么东东呢,肯定是key-value啊,key就是上面代码中的共享静态变量 dfThreadLocal,value就是DateFormat实例了,即new SimpleDateFormat(dateFormat)这个东东。那接下来,在线程内我要如何去获取这个值呢,就是靠dfThreadLocal.get()实现滴,方法源码如下:

ThreadLocal .ThreadLocalMap inheritableThreadLocals = null ;
  public T get () {
     Thread t = Thread.currentThread ();
     ThreadLocalMap map = getMap(t );
     if ( map != null) {
          ThreadLocalMap.Entry e = map.getEntry (this);
         if ( e != null)
             return ( T)e .value;
     }
    return setInitialValue ();
}
ThreadLocalMap getMap (Thread t) {
    return t .inheritableThreadLocals;
}

可以很明显的看出,首先根据Thread.currentThread ()获取到inheritableThreadLocals(即ThreadLocalMap,他是Thread的一个变量),然后将this(即最上面代码的dfThreadLocal对象)作为key(或索引)获取到真正的值T(就是SimpleDateFormat对象)啊,至此应该比较清楚了。

    为什么不同的线程有各自的值,因为 不同的线程--->不同的ThreadLocalMap对象(线程的变量)--->通过相同的key(如果有被static修饰)获取到不同的value值。

  备注:一般都被static修饰,因为可以避免在一个线程内可能发生的重复创建TSO(Thread Specific Object,即ThreadLocal所关联的对象),被statis修饰了,同一线程key也肯定一样,value也肯定只有一份了。

 一个ThreadLocal实例关联当前线程的一个TSO对象,如果把ThreadLocal声明为实例变量,那么每创建一个类实例就会导致一个TSO实例诞生,这肯定没有这个必要滴。

具体实现

获取session的工具类


	
package com.platform.framework.session.impl;

import com.platform.framework.context.ThreadContextHolder;
import com.platform.inf.ISessionService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class HttpSessionService
  implements ISessionService
{
  private final Log logger = LogFactory.getLog(getClass());
  
  public Object getAttribute(String arg0)
  {
    HttpSession session = getSession();
    return session == null ? null : session.getAttribute(arg0);
  }
  
  public String getId()
  {
    HttpSession session = getSession();
    return session == null ? null : session.getId();
  }
  
  public void invalidate()
  {
    HttpSession session = getSession();
    if (session != null) {
      session.invalidate();
    }
  }
  
  public void removeAttribute(String arg0)
  {
    HttpSession session = getSession();
    if (session != null) {
      session.removeAttribute(arg0);
    }
  }
  
  public void setAttribute(String arg0, Object arg1)
  {
    HttpSession session = getSession();
    if (session != null) {
      session.setAttribute(arg0, arg1);
    }
  }
  
  private HttpSession getSession()
  {
    HttpServletRequest request = ThreadContextHolder.getHttpRequest();
    if ((request == null) || (request.getSession() == null)) {
      this.logger.info("============================>>>sessoin 失效");
    }
    return request.getSession();
  }
}
package com.platform.framework.context;

import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class ThreadContextHolder
{
  private static ThreadLocal HttpRequestThreadLocalHolder = new ThreadLocal();
  private static ThreadLocal HttpResponseThreadLocalHolder = new ThreadLocal();
  private static ThreadLocal threadVar = new ThreadLocal();
  
  public static void setThreadValue(String key, Object value)
  {
    Map map = (Map)threadVar.get();
    if (map == null)
    {
      map = new HashMap();
      map.put(key, value);
      threadVar.set(map);
    }
    else
    {
      map.put(key, value);
    }
  }
  
  public static  T getThreadValue(String key)
  {
    Map map = (Map)threadVar.get();
    if (map != null) {
      return map.get(key);
    }
    return null;
  }
  
  public static void setHttpRequest(HttpServletRequest request)
  {
    HttpRequestThreadLocalHolder.set(request);
  }
  
  public static HttpServletRequest getHttpRequest()
  {
    return (HttpServletRequest)HttpRequestThreadLocalHolder.get();
  }
  
  public static void setHttpResponse(HttpServletResponse response)
  {
    HttpResponseThreadLocalHolder.set(response);
  }
  
  public static HttpServletResponse getHttpResponse()
  {
    return (HttpServletResponse)HttpResponseThreadLocalHolder.get();
  }
  
  public static String getSessionId()
  {
    HttpServletRequest request = (HttpServletRequest)HttpRequestThreadLocalHolder.get();
    if (null != request) {
      return request.getSession().getId();
    }
    return null;
  }
  
  public static void clearThreadValues()
  {
    threadVar.remove();
  }
}
	public String getDict(String dictCode, String value) {
		Dictionary dict = getDict(dictCode);
		if (dict == null || value == null)
			return null;
		List dictionaryItems = dict.getDictItems();
		
		if(dictionaryItems == null && dict.getDictType() == Constant.NUMBER_INTEGER_1) { 		//内部字典,但字典项为null(单独获取内部字典项,并放入缓存中)
			dictionaryItems = dictContextService.getDictItems(dict);
			dict.setDictItems(dictionaryItems);
		}else if(dict.getDictType() == Constant.NUMBER_INTEGER_2) {								//外部字典(获取字典项从线程缓存中)
			Dictionary odict = ThreadContextHolder.getThreadValue(dictCode);
			if(odict == null) {
				dictionaryItems = dictContextService.getDictItems(dict);
				dict.setDictItems(dictionaryItems);
				ThreadContextHolder.setThreadValue(dict.getDictCode(), dict);
			}else {
				dictionaryItems = odict.getDictItems();
			}
		}
		
	@ResponseBody
	@RequestMapping(value = "/loginCheck", method = RequestMethod.POST)
	public AssembleJSON loginCheck(Model model,HttpServletRequest request) {
		try {
			String userCode = request.getParameter("userCode");
			String sender = request.getParameter("userCode") + request.getParameter("password");
			String EncryptedStr = MD5Util.MD5Encrypted(sender);
			String str = userService.checkUser(userCode);
			if (LoginConstant.LOGIN_USER_NOTEXIST_CODE.equals(str)) { // 用户不存在
				return AssembleJSON.SUCCESS(Integer.valueOf(LoginConstant.LOGIN_USER_NOTEXIST_CODE),
						LoginConstant.LOGIN_NOTEXIST_STRING);
			}
			if (str == LoginConstant.LOGIN_USER_INVALID_CODE) { // 无效用户
				return AssembleJSON.SUCCESS(Integer.valueOf(LoginConstant.LOGIN_USER_INVALID_CODE),
						LoginConstant.LOGIN_USER_INVALID_STRING);
			}
			if (str == LoginConstant.LOGIN_USER_LOCKED_CODE) { // 锁定用户
				return AssembleJSON.SUCCESS(Integer.valueOf(LoginConstant.LOGIN_USER_LOCKED_CODE),
						LoginConstant.LOGIN_USER_LOCKED_STRING);
			}
			String verifyCode = request.getParameter("verifyCode");
			String code = (String) request.getSession().getAttribute("verCode");
			if (null == code) { // 验证码过期
				return AssembleJSON.SUCCESS(Integer.valueOf(LoginConstant.LOGIN_VERIFYCODE_OUTDATE_CODE),
						LoginConstant.LOGIN_VERIFYCODE_OUTDATE_STRING);
			}
			if (null != code && verifyCode.toLowerCase().equals(code.toLowerCase())) {
				if (EncryptedStr.equals(str)) {
					User user = userService.getUserByCode(userCode);
					user.setUserPass(request.getParameter("password"));
					request.getSession(true).setAttribute(LoginConstant.LOGIN_USER_SESSION_KEY, user);
					ThreadContextHolder.setHttpRequest(request); // 将当前登录 Request 放入线程变量
					return AssembleJSON.SUCCESS(user);
				} else { // 用户密码错误
					return checkLoginNum(request,userCode);
				}
			} else { // 验证码错误
				return AssembleJSON.SUCCESS(Integer.valueOf(LoginConstant.LOGIN_VERIFYCODE_ERROR_CODE),
						LoginConstant.LOGIN_VERIFYCODE_ERROR_STRING);
			}
		}finally{
			try {
				User user = (User) request.getSession().getAttribute(LoginConstant.LOGIN_USER_SESSION_KEY);
				if(user != null) {
					Log log = new Log();
					log.setLogUserCode(user.getUserCode());
					log.setLogUserName(user.getUserName());
					log.setLogType(Constant.LOG_TYPE_LOGIN);
					log.setLogTime(new Date());
					log.setLogIp(request.getRemoteAddr());
					logService.insertLog(log);			// 添加登录记录到系统日志表
				 }
				}catch(Exception e) {
					log.error(e.getMessage());
				}
			}
	}



取线程中的中的user

User user = (User)ThreadContextHolder.getHttpRequest().getSession().getAttribute("current_login_user");

实现request anywhere

  import com.platform.core.web.Request;
  public class AppMgr {
   /**
     * 线程级变量
     */
    private static final ThreadLocal TL = new ThreadLocal();

    /**
     * 获取线程级实例对象
     * @return rtnBean rtnBean
     */
    private static Bean threadBean() {
        Bean bean = (Bean) TL.get();
        if (bean == null) {
            bean = new Bean();
            TL.set(bean);
        }
        return bean;
    }

    /**
     * 获取线程级实例对象中某参数值
     * @param key key
     * @return rtnObj rtnObj
     */
    public static Object threadVar(String key) {
        return threadBean().get(key);
    }

    /**
     * 设置线程级实例对象中某参数值
     * @param key key
     * @param obj setter objInst
     */
    public static void setThreadVar(String key, Object obj) {
        threadBean().set(key, obj);
    }


   /**
     * 参数BEAN键
     */
    public static final String KEY_PARAM_BEAN = "$PARAM_BEAN";

    /**
     * 参数值
     * @param key
     * @return Object
     */
    public static Object paramVar(String key) {
        IBean bean = (IBean) threadVar(KEY_PARAM_BEAN);
        if (bean != null) {
            return bean.get(key);
        } else {
            return null;
        }
    }
}
public class Request {
    /**
	 * 获取Request实例
	 *
	 * @return Request实例对象
	 */
	public static HttpServletRequest getInst() {
		return (HttpServletRequest) AppMgr.threadVar("request");
	}

	/**
	 * @param request
	 *            void
	 */
	public static void setInst(HttpServletRequest request) {
		AppMgr.setThreadVar("request", request);
	}

	/**
	 * 返回当前请求的Session
	 *
	 * @param create
	 *            没有有效的Session时,是否创建新Session,不创建返回null
	 * @return Session
	 */
	public static HttpSession getSession(boolean create) {
		return getInst().getSession(create);
	}

	/**
	 * 返回当前请求的Session
	 *
	 * @return Session
	 */
	public static HttpSession getSession() {
		return getInst().getSession();
	}
}

代理线程中的HttpServletRequest变量,使代码中可以通过静态方法访问request

      var namesalt = getNowFormatDate();
		
		var strUrl = Leopard.getContextPath() +
		"/DoMyServlet?className=ExcelPoiAction&methodName=createExcel&btnCode=empdata&forWard=isFile&namesalt="+namesalt+"&func="+_func
	    +"&pbean="+encodeURI(encodeURI(strwhere))+"&btnCode"+empexcel;
		var ifm;
		if (document.getElementById("empexcel_iframe") == undefined) {
			ifm = document.createElement("IFRAME");
			ifm.setAttribute("id", "empexcel_iframe");
			ifm.setAttribute("name", "empexcel_iframe");
			ifm.style.height = "0";
			ifm.style.width = "0";
			ifm.style.display = "block";
			ifm.src = "";
			document.body.appendChild(ifm);
			document.getElementById("empexcel_iframe").attachEvent(
					"onload",
					function() {
						window.frames['empexcel'].document.getElementById("empexcel").click();
					});
		} else {				ifm = document.getElementById("empexcel_iframe");			}
		ifm.src = strUrl; 


public class ExcelPoiAction {
public void createExcel() throws IOException {
HttpServletRequest req = Request.getInst();
this.funcCode = req.getParameter("func");		//功能单元
//this.strWhere = req.getParameter("pbean");		//附件查询条件
this.strWhere =	java.net.URLDecoder.decode(req.getParameter("pbean"),"utf-8");
if (!StringUtils.isEmpty(strWhere)) {
try {
objWhere = new JSONObject("{" + strWhere + "}");
} catch (JSONException e) {}
}
//获取业务参数
String busiStr = req.getParameter("busiData");
if(!StringUtils.isEmpty(busiStr)){
try {
this.busiData = JsonUtils.transferToBean(busiStr);
} catch (Exception e) {}
}
}

线程共享变量缓存如下:

Thread.ThreadLocalMap;

1、Thread: 当前线程,可以通过Thread.currentThread()获取。

2、ThreadLocal:我们的static ThreadLocal变量。

3、Object: 当前线程共享变量。

我们调用ThreadLocal.get方法时,实际上是从当前线程中获取ThreadLocalMap,然后根据当前ThreadLocal获取当前线程共享变量Object。

ThreadLocal.set,ThreadLocal.remove实际上是同样的道理。

这种存储结构的好处:

1、线程死去的时候,线程共享变量ThreadLocalMap则销毁。

2、ThreadLocalMap键值对数量为ThreadLocal的数量,一般来说ThreadLocal数量很少,相比在ThreadLocal中用Map键值对存储线程共享变量(Thread数量一般来说比ThreadLocal数量多),性能提高很多。

关于ThreadLocalMap弱引用问题:

当线程没有结束,但是ThreadLocal已经被回收,则可能导致线程中存在ThreadLocalMap的键值对,造成内存泄露。(ThreadLocal被回收,ThreadLocal关联的线程共享变量还存在)。

虽然ThreadLocal的get,set方法可以清除ThreadLocalMap中key为null的value,但是get,set方法在内存泄露后并不会必然调用,所以为了防止此类情况的出现,我们有两种手段。

1、使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;

2、JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。

源码实现

ThreadLocal类提供的几个方法:


    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();

--------------------- 
 ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

--------------------- 
ThreadLocal.ThreadLocalMap threadLocals = null;

--------------------- 

static class ThreadLocalMap {
 
        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference            
关注
打赏
1665965058
查看更多评论
0.0497s