目的
session存儲在緩存服務器上(各種緩存服務器上均可,本文以memcached為例),但對開發者來說,他不用關注,只需要調用request.getSession()方法即可獲取到session,然后對session的屬性進行操作。
面臨的問題
1. session獲取,不是從application的服務器上獲取,要從memcached上獲取。
2. session屬性的獲取及設置,不是設置到application服務器上,而是操作memcached獲取或者設置。
解決問題的方法
1. 使用一個HttpServletRequestWrapper的實現類,重寫getSession()方法,然后使用filter,來過濾每個請求,使request變為requestWrapper。
2. 使用一個HttpSessionAttributeListener的實現類,重寫attributeAdded()、attributeRemoved()、attributeReplaced()方法,當屬性發生改變時需要通知memcached中的session發生改變
另外:為解決各個異構系統因語言不通可能發生的兼容問題,session以json字符串存儲。
具體代碼如下:
wrapper類
import java.io.IOException; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpSession; import org.springframework.util.StringUtils; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.javacodegeeks.util.JacksonMapUtil; import com.javacodegeeks.util.MemcachedUtil; public class GetSessionWrapper extends HttpServletRequestWrapper{ private String sessionId=null; public GetSessionWrapper(HttpServletRequest request) { super(request); } public GetSessionWrapper(HttpServletRequest request,String sessionId) { super(request); this.setSessionId(sessionId); } @Override public HttpSession getSession() { HttpSession httpSession=super.getSession(); //id-->sessionId; String id="davidwang456"; String json=MemcachedUtil.getValue(id); if(StringUtils.isEmpty(json)){ return httpSession; } httpSession.setAttribute("JPHPSESSID", id); // 讀取JSON數據 Map<String, Object> userData; try { userData = JacksonMapUtil.getMapper().readValue(json, Map.class); for(Map.Entry<String, Object> entry:userData.entrySet()){ httpSession.setAttribute(entry.getKey(), entry.getValue()); } } catch (JsonParseException e) { System.out.println("json字符串不能解析成功!"); } catch (JsonMappingException e) { System.out.println("json字符串不能映射到Map!"); } catch (IOException e) { System.out.println("io異常!"); } return httpSession; } @Override public HttpSession getSession(boolean create) { HttpSession httpSession=super.getSession(create); return httpSession; } public static void main(String[] args) { String sessionId="davidwang456"; String json=MemcachedUtil.getValue(sessionId); System.out.println(json); } public String getSessionId() { return sessionId; } public void setSessionId(String sessionId) { this.sessionId = sessionId; } }
filter類
import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import org.springframework.web.util.WebUtils; public class FetchSession implements javax.servlet.Filter{ private static final String regex=".*(css|html|ico|html|jpg|jpeg|png|gif|js)"; @Override public void init(FilterConfig filterConfig) throws ServletException { } private static String getSessionId(ServletRequest request){ HttpServletRequest httpRequest=(HttpServletRequest)request; String sessionId=""; Cookie cookie =WebUtils.getCookie(httpRequest, "PHPSESSID"); if(cookie!=null){ return cookie.getValue(); } cookie =WebUtils.getCookie(httpRequest, "JSESSIONID"); if(cookie!=null){ sessionId= cookie.getValue(); } return sessionId; } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String sessionId=getSessionId(request); HttpServletRequest httpRequest=(HttpServletRequest)request; String requestedUri=httpRequest.getRequestURL().toString(); System.out.println(requestedUri); if(requestedUri.matches(regex)){ chain.doFilter(request, response); return; } GetSessionWrapper wrapperRequest=new GetSessionWrapper(httpRequest,sessionId); //HttpSession httpSession=wrapperRequest.getSession(); chain.doFilter(wrapperRequest, response); } @Override public void destroy() { } }
屬性監聽器
import java.io.IOException; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; import org.springframework.util.StringUtils; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.javacodegeeks.util.JacksonMapUtil; import com.javacodegeeks.util.MemcachedUtil; public class MySessionAttributeListener implements HttpSessionAttributeListener { private static AtomicInteger count=new AtomicInteger(0); private static AtomicInteger countU=new AtomicInteger(0); @Override public void attributeAdded(HttpSessionBindingEvent event) { int ss=count.incrementAndGet(); HttpSession session=event.getSession(); //String sessionId=(String) session.getAttribute("JPHPSESSID"); String sessionId="davidwang456"; String attributeName = event.getName(); Object attributeValue = event.getValue(); System.out.println("Attribute add " + attributeName + " : " + attributeValue+",ss="+ss); String json=MemcachedUtil.getValue(sessionId); if(StringUtils.isEmpty(json)){ return ; } String json_new; try { json_new = attributeAddOrUpdate(json,attributeName,attributeValue); MemcachedUtil.setValue(sessionId, json_new); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } private String attributeAddOrUpdate(String json,String key,Object value) throws JsonParseException, JsonMappingException, IOException{ ObjectMapper mapper=JacksonMapUtil.getMapper(); @SuppressWarnings("unchecked") Map<String,Object> userData = mapper.readValue(json, Map.class); Boolean flag=String.class.isAssignableFrom(value.getClass()); if(!flag){ Map<String, Object> map = mapper.convertValue(value, Map.class); userData.putAll(map); }else{ userData.put(key, value); } return mapper.writeValueAsString(userData); } private String attributeDel(String json, String key) throws JsonParseException, JsonMappingException, IOException { ObjectMapper mapper = JacksonMapUtil.getMapper(); @SuppressWarnings("unchecked") Map<String, Object> userData = mapper.readValue(json, Map.class); userData.remove(key); return mapper.writeValueAsString(userData); } @Override public void attributeRemoved(HttpSessionBindingEvent event) { HttpSession session=event.getSession(); //String sessionId=(String) session.getAttribute("JPHPSESSID"); String sessionId="davidwang456"; String attributeName = event.getName(); System.out.println("Attribute del : " + attributeName); String json=MemcachedUtil.getValue(sessionId); if(StringUtils.isEmpty(json)){ return ; } String json_new; try { json_new = attributeDel(json,attributeName); MemcachedUtil.setValue(sessionId, json_new); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void attributeReplaced(HttpSessionBindingEvent event) { int ssu=countU.incrementAndGet(); HttpSession session=event.getSession(); //String sessionId=(String) session.getAttribute("JPHPSESSID"); String sessionId="davidwang456"; String attributeName = event.getName(); Object attributeValue = event.getValue(); System.out.println("Attribute update " + attributeName + " : " + attributeValue+",ss="+ssu); String json=MemcachedUtil.getValue(sessionId); if(StringUtils.isEmpty(json)){ return ; } String json_new; try { json_new = attributeAddOrUpdate(json,attributeName,attributeValue); MemcachedUtil.setValue(sessionId, json_new); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
pom.xml依賴:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.4.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.4.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-guava</artifactId> <version>2.4.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-joda</artifactId> <version>2.4.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.4.3</version> </dependency> <dependency> <groupId>net.spy</groupId> <artifactId>spymemcached</artifactId> <version>2.12.0</version> </dependency>
注意:上面代碼僅為demo代碼,實際應用需重構代碼。