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
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<ThreadLocal, Object>;
1、Thread: 當前執行緒,可以通過Thread.currentThread()獲取。
2、ThreadLocal:我們的static ThreadLocal變數。
3、Object: 當前執行緒共享變數。
我們呼叫ThreadLocal.get方法時,實際上是從當前執行緒中獲取ThreadLocalMap<ThreadLocal, Object>,然後根據當前ThreadLocal獲取當前執行緒共享變數Object。
ThreadLocal.set,ThreadLocal.remove實際上是同樣的道理。
這種儲存結構的好處:
1、執行緒死去的時候,執行緒共享變數ThreadLocalMap則銷燬。
2、ThreadLocalMap<ThreadLocal,Object>鍵值對數量為ThreadLocal的數量,一般來說ThreadLocal數量很少,相比在ThreadLocal中用Map<Thread, Object>鍵值對儲存執行緒共享變數(Thread數量一般來說比ThreadLocal數量多),效能提高很多。
關於ThreadLocalMap<ThreadLocal, Object>弱引用問題:
當執行緒沒有結束,但是ThreadLocal已經被回收,則可能導致執行緒中存在ThreadLocalMap<null, Object>的鍵值對,造成記憶體洩露。(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);
}