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

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-config>的子标签 <session.timeout>,单位为分钟,主要是针对整个应用的所有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
 * <p>2009-12-17 下午03:10:09</p>
 * @version 1.1
 * 新增request any where
 */
public class ThreadContextHolder  {
 
protected static final Logger logger = Logger.getLogger(ThreadContextHolder.class);
 
private static ThreadLocal<WebSessionContext> SessionContextThreadLocalHolder = new ThreadLocal<WebSessionContext>();
private static ThreadLocal<HttpServletRequest> HttpRequestThreadLocalHolder = new ThreadLocal<HttpServletRequest>();
private static ThreadLocal<HttpServletResponse> HttpResponseThreadLocalHolder = new ThreadLocal<HttpServletResponse>();
 
 
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

扫描二维码关注公众号,回复: 4523742 查看本文章

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<DateFormat> dfThreadLocal=new ThreadLocal<DateFormat>(){
      @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的工具类

<!-- http session 处理类 -->
	<bean id="httpSessionService" class="com.session.impl.HttpSessionService" lazy-init="false"/>
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<HttpServletRequest> HttpRequestThreadLocalHolder = new ThreadLocal();
  private static ThreadLocal<HttpServletResponse> HttpResponseThreadLocalHolder = new ThreadLocal();
  private static ThreadLocal<Map<String, Object>> threadVar = new ThreadLocal();
  
  public static void setThreadValue(String key, Object value)
  {
    Map<String, Object> 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> T getThreadValue(String key)
  {
    Map<String, Object> 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<DictionaryItem> 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<ThreadLocalObject>;

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

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

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

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

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

这种存储结构的好处:

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

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

关于ThreadLocalMap<ThreadLocalObject>弱引用问题:

当线程没有结束,但是ThreadLocal已经被回收,则可能导致线程中存在ThreadLocalMap<nullObject>的键值对,造成内存泄露。(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<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;
 
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
--------------------- 

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
--------------------- 
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

request和response

Servlet 

   说明servlet主要有三个生命周期:

     ①Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。

  ②装载并创建该Servlet的一个实例对象,注意这个实例是单例的,所以调用后面的service方法的时候可能有并发问题。
  ③调用Servlet实例对象的init()方法。
  ④创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
  ⑤WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。

    关于servlet线程安全的问题这里多提一下:

    对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。只要该Servlet中不存在全局变量就不存在线程安全问题,因为每个线程访问的时候都会有自己的方法栈,局部变量是互不影响的。

web.xml

<servlet>  
    <servlet-name>ServletDemo</servlet-name>  
    <servlet-class>com.ServletDemo</servlet-class>  
  </servlet>  
  
  <servlet-mapping>  
    <servlet-name>ServletDemo</servlet-name>  
    <url-pattern>/servlet/ServletDemo</url-pattern>  
  </servlet-mapping>  
protected void service(HttpServletRequest req, HttpServletResponse resp)  
       throws ServletException, IOException  
   {  
       String method = req.getMethod();  
       if(method.equals("GET"))  
       {  
           long lastModified = getLastModified(req);  
           if(lastModified == -1L)  
           {  
               doGet(req, resp);  
           } else  
           {  
               long ifModifiedSince = req.getDateHeader("If-Modified-Since");  
               if(ifModifiedSince < (lastModified / 1000L) * 1000L)  
               {  
                   maybeSetLastModified(resp, lastModified);  
                   doGet(req, resp);  
               } else  
               {  
                   resp.setStatus(304);  
               }  
           }  
       } else  
       if(method.equals("HEAD"))  
       {  
           long lastModified = getLastModified(req);  
           maybeSetLastModified(resp, lastModified);  
           doHead(req, resp);  
       } else  
       if(method.equals("POST"))  
           doPost(req, resp);  
       else  
       if(method.equals("PUT"))  
           doPut(req, resp);  
       else  
       if(method.equals("DELETE"))  
           doDelete(req, resp);  
       else  
       if(method.equals("OPTIONS"))  
           doOptions(req, resp);  
       else  
       if(method.equals("TRACE"))  
       {  
           doTrace(req, resp);  
       } else  
       {  
           String errMsg = lStrings.getString("http.method_not_implemented");  
           Object errArgs[] = new Object[1];  
           errArgs[0] = method;  
           errMsg = MessageFormat.format(errMsg, errArgs);  
           resp.sendError(501, errMsg);  
       }  
   }  

Request

1.获取请求的基本信息

1>获取请求的url和uri

2>获取url后面的请求参数部分的字符串

3>获取请求方式

4>获取主机名,IP地址

5>获取 Contexpath

String url = request.getRequestURL().toString();
System.out.println(url);
String uri = request.getRequestURI().toString();
System.out.println(uri);
String params = request.getQueryString();
System.out.println(params);
String method = request.getMethod();
System.out.println(method);
String addr = request.getRemoteHost() + request.getRemotePort() + request.getRemoteAddr() +
    "==user=" + request.getRemoteUser();
System.out.println("addr: " + addr);
String contextPath = request.getContextPath();
response.sendRedirect(contextPath + "/index.jsp");

获取请求参数

Map<String, String[]> params = request.getParameterMap();
for (String name : params.keySet()) {
   String value = request.getParameter(name);
   System.out.println("name=" + name + ", value=" + value);
}

解决请求乱码问题:

request.setCharacterEncoding("Utf-8");

如果上面的代码只能解决POST的乱码问题, 则可以自行进行解码操作

String userName = request.getParameter("username");
userName =  new String(userName.getBytes("ISO8859-1"), "UTF-8");

前段使用 UTF-8 进行编码, 传输到服务器, 服务器可以使用 ISO8859-1 解码得到UTF-8编码后的码值, 然后通过new String(bytes, charset)的方式进行解码

设置和获取域属性

Object attr = request.getAttribute("attr");
request.setAttribute("key", "value");
request.removeAttribute("attr");
Enumeration<String> attributeNames = request.getAttributeNames();

一般我们的应用是servlet处理数据, 将处理好的数据放到request域中,然后带到jsp页面上进行展示操作.

请求转发与请求包含

请求转发:

request.getRequestDispatcher("/DispatcherTest2").forward(request, response);

this.getServletContext().getRequestDispatcher("/DispatcherTest2").forward(request, response);

1)一次请求只能转发一次, 否则会发生下面的异常: -- 可以得到第一次转发获取的数据
java.lang.IllegalStateException: Cannot forward after response has been committed
2)当有数据已经写到客户端时再请求转发也会抛出异常.
3)若转发前有数据写入到response缓冲区,则请求转发会清空response缓冲区的实体内容, 但不会清空请求头信息.

请求包含:

当需要将多个servlet的输出合并到一块打给浏览器时可以使用请求包含

request.getRequestDispatcher("/DispatcherTest2").include(request, response);
或
this.getServletContext().getRequestDispatcher("/DispatcherTest2").include(request, response);

    1)被包含的Servlet程序不能改变响应消息的状态码和响应头,如果它里面存在这样的语句,这些语句的执行结果将被忽略.

    2)常被用来进行页面布局

response.sendRedirect(request.getContextPath() + "/DispatcherTest2");

1) 不能在数据已经发送到浏览器之后再进行请求重定向:
java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed

2) 在请求重定向之前写入到response缓冲区的数据会被清空
3) 一次请求只能重定向一次

请求重定向地址栏会发生变化.请求转发地址栏不发生变化.
请求重定向两次请求两次响应.请求转发一次请求一次响应.

如果需要在资源跳转时利用request域传递域属性则必须使用请求转发
如果希望资源跳转后修改用户的地址栏则使用请求重定向

如果使用请求转发也可以重定向也可以,则优先使用请求转发,减少浏览器对服务器的访问次数减轻服务器的压力.

response
    ServletResponse -- 通用的response提供了一个响应应该具有最基本的属性和方法
        |
        |-HttpServletResponse -- 在ServletResponse的基础上针对于HTTP协议增加了很多强化的属性和方法

2.输出数据
 1)getOutputStream字节输出流

response.getOutputStream().write("中国".getBytes("utf-8"));

string.getBytes()如果没有指定编码方式,会使用平台默认的编码方式

2)getWriter字符输出流

response.getWriter().write("北京");

getWriter和getOutputStream在一次请求中只能使用一个
    使用字符输出流输出中文时, 由于网线上只能输出高低电平,如果没有指定编码方式,那么服务器在发送数据时会使用默认的ISO-8859-1对数据编码(该码表中没有汉字, 因此汉字会被编码为?, 传送到浏览器上的数据实际就是?).

解决乱码

    1> 通知服务器发送数据时使用utf-8编码

response.setCharacterEncoding("utf-8");

  2> 通知浏览器接受数据时使用utf-8解码

response.setHeader("Content-Type", "text/html;charset=utf-8");

 a. response对象中对Content-Type响应头进行了封装,可以使用一下代码代替 2>

response.setContentType("text/html;charset=utf-8");

   b. 如果设置了Content-Type,服务器会自动的设置 characterEncoding,因此解决乱码只需要设置Content-Type响应头一行代码就可以了,但是为了代码的可读性更高,一般还是建议同时设置 characterEncoding 和 Content-Type.
3)实现下载

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("美女.jpg"));
    InputStream in = new FileInputStream(this.getServletContext().getRealPath("美女.jpg"));
    OutputStream out = response.getOutputStream();
    byte[] bytes = new byte[1024];
    int len = -1;
    while(-1 != (len = in.read(bytes))) {
       out.write(bytes, 0, len);
    }
    in.close();
}

4) 实现定时刷新

Servlet实现

response.setContentType("text/html;charset=utf-8");
response.getWriter().write("恭喜您注册成功, 3秒后回到主页");
response.setHeader("Refresh", "3;url=OutServlet");

html实现

<meta charset="UTF-8">
<meta http-equiv="Refresh" content="3; url=index.jsp" >
<title>Insert title here</title>
</head>
<body>
    恭喜您注册成功, 3秒后回到主页....
</body>

5)控制浏览器是否缓存

response.setIntHeader("Expires", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.getWriter().write(new Date().toLocaleString());

6)实现请求重定向

response.sendRedirect(this.getServletContext().getContextPath());
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<HttpServletRequest> HttpRequestThreadLocalHolder = new ThreadLocal();
  private static ThreadLocal<HttpServletResponse> HttpResponseThreadLocalHolder = new ThreadLocal();
  private static ThreadLocal<Map<String, Object>> threadVar = new ThreadLocal();
  
  public static void setThreadValue(String key, Object value)
  {
    Map<String, Object> 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> T getThreadValue(String key)
  {
    Map<String, Object> 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();
  }
}
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();
  }
}
public class SessionContextHolder
{
  private static ISessionService sessionService;
  
  public static ISessionService getInstance()
  {
    sessionService = (ISessionService)SpringContextHolder.getBean("httpSessionService");
    return sessionService;
  }
}
 <!-- bean 管理器 -->
	<bean id="springContextHolder" class="com.platform.framework.context.SpringContextHolder" lazy-init="false"/>
	public List<Map<String, Object>> getOrgUsers(){
		User user = (User) SessionContextHolder.getInstance().getAttribute(LoginConstant.LOGIN_USER_SESSION_KEY);
		return userMapper.selectOrgUsers(user);
	}

猜你喜欢

转载自blog.csdn.net/qq_35029061/article/details/84849952