Spring-session Introduction and implementation principle source code analysis, spring-session source code

Source: Internet
Author: User

Spring-session Introduction and implementation principle source code analysis, spring-session source code

I. Introduction to spring-session

1. Introduction

Session has always been a problem that needs to be solved when we build a cluster. In the past, we could solve it from the serlvet container, for example, the open-source servlet container-tomcat provides tomcat-redis-session-manager and memcached-session-manager.

Or use nginx or another Server Load balancer to do ip_hash and route it to a specific server ..

However, both methods have drawbacks.

Spring-session is a project under spring. It replaces the httpSession implemented by the servlet container with spring-session and focuses on solving session management issues. It can be easily, quickly, and seamlessly integrated into our applications.

2. Supported Functions

1) The session is easily stored in a third-party storage container. The framework provides redis, jvm map, mongo, gemfire, hazelcast, jdbc, and other containers that store sessions.

2) The same browser and the same website support multiple session problems.

3) RestfulAPI does not rely on cookies. You can pass the jessionID through the header.

4) combine WebSocket and spring-session to synchronize lifecycle management.

3. Integration Method

The integration method is very simple. You can directly view samplesandguide on the official website. Http://docs.spring.io/spring-session/docs/1.3.0.RELEASE/reference/html5/

It mainly includes the following integration steps:

1) introduce the dependent jar package

2) configure the storage mode of a specific storage container in annotation or xml mode, such as the xml configuration mode of redis.

<Context: annotation-config/>/** Initialize all spring-session preparations and put springSessionFilter into IOC **/<beanclass = "org. springframework. session. data. redis. config. annotation. web. http. redisHttpSessionConfiguration "/>/** this is the connection pool of the storage container **/<beanclass =" org. springframework. data. redis. connection. lettuce. lettuceConnectionFactory "/>

3) Configure web. xml in xml mode, and configure springSessionFilter to filter chain.

<filter>     <filter-name>springSessionRepositoryFilter</filter-name>     <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>    </filter>    <filter-mapping>     <filter-name>springSessionRepositoryFilter</filter-name>     <url-pattern>/*</url-pattern>     <dispatcher>REQUEST</dispatcher><dispatcher>ERROR</dispatcher>    </filter-mapping>

Ii. internal analysis of the spring-session framework

1. High-level abstract structure of the Framework

2. spring-session rewrite servlet request and redis implementation storage problems

The general principle of spring-session seamless replacement of application server requests is:
1. Customize filters to implement the doFilter Method
2. inherit from the HttpServletRequestWrapper and HttpServletResponseWrapper classes, and override getSession and other related methods (call related session storage container operation classes in these methods ).
3. In the doFilter in step 1, the new request and response class customized in step 2. And pass them to the filter chain respectively.
4. Configure the filter to the first position of the filter chain.

/** This class is the source code 1.30 of spring-session, it is also the key class for implementing the first to third steps **/public class SessionRepositoryFilter <S extends ExpiringSession> extends OncePerRequestFilter {/** session storage container interface, redis, mongoDB, genfire, and other databases all implement this interface **/private final SessionRepository <S> sessionRepository; private ServletContext servletContext;/** sessionID transfer method interface. Currently, spring-session comes with two implementation classes: 1. cookie: CookieHttpSessionStrategy 2. http header: HeaderHttpSessionStrategy. Of course, we can also customize other methods. **/Private MultiHttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy (); public SessionRepositoryFilter (SessionRepository <S> sessionRepository) {if (sessionRepository = null) {throw new IllegalArgumentException ("sessionRepository cannot be null");} this. sessionRepository = sessionRepository;} public void setHttpSessionStrategy (HttpSessionStrategy httpSessionStrategy) {if (HttpSessionStrategy = null) {throw new IllegalArgumentException ("httpSessionStrategy cannot be null");}/** through the previous spring-session function introduction, we know that spring-session supports multiple sessions in a single browser, which is achieved through MultiHttpSessionStrategyAdapter. Each browser has a sessionID, but this sessionID has multiple aliases (based on the tab of the browser ). For example, alias 1 sessionID alias 2 sessionID... this alias is passed through the url. this is the principle of multiple sessions in a single browser. **/this. httpSessionStrategy = new Strategy (httpSessionStrategy);} public void strategy (MultiHttpSessionStrategy httpSessionStrategy) {if (httpSessionStrategy = null) {throw new Strategy ("httpSessionStrategy cannot be null");} this. httpSessionStrategy = httpSessionStrategy; }/** This method overwrites doFilter, but spring-session encapsulates multiple layers. Create a custom request and response in this method, and pass it to the filter chain filterChain **/@ Override protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {request. setAttribute (SESSION_REPOSITORY_ATTR, this. sessionRepository);/** ServletRequest rewritten by spring-session. This class inherits from HttpServletRequestWrapper **/SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper (request, response, this. servletContext); SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper (wrappedRequest, response); HttpServletRequest strategyRequest = this. httpSessionStrategy. wrapRequest (wrappedRequest, wrappedResponse); HttpServletRe Else se strategyResponse = this. httpSessionStrategy. wrapResponse (wrappedRequest, wrappedResponse); try {/** to pass the custom request and response to the chain. Imagine if the spring-sessionFilter is located at the first in the filter chain, so the subsequent filters, as well as the requests and response obtained at the final control layer, are we customizing them? **/FilterChain. doFilter (strategyRequest, strategyResponse);} finally {wrappedRequest. commitSession () ;}} public void setServletContext (ServletContext servletContext) {this. servletContext = servletContext;}/** this is the rewrite class of Servlet response */private final class SessionRepositoryResponseWrapper extends OnCommittedResponseWrapper {private final SessionRepositoryRequestWrapper request SessionRep; OsitoryResponseWrapper (response request, HttpServletResponse response) {super (response); if (request = null) {throw new IllegalArgumentException ("request cannot be null");} this. request = request;}/** in this step, the session is persistent to the storage container, we may call the session operation method multiple times in a control layer. If each session operation is persistent to the storage container, the performance will inevitably be affected. For example, redis, so we can complete the execution at the entire control layer. Only session **/@ Override protected void onResponseCommitted () {this will be persistent when response returns information to the browser. request. commitSession () ;}}/** request rewriting class of spring-session, which is almost the most important rewrite class. It overwrites getSession, Session, and other methods and Classes */private final class extends {private Boolean requestedSessionIdValid; private boolean requestedSessionInvalidated; private final HttpServletResponse response; private final ServletContext servletContext; private SessionRepositoryRequestWrapper (HttpServletRequest request, HttpServletResponse response, Servl EtContext servletContext) {super (request); this. response = response; this. servletContext = servletContext;}/*** Uses the HttpSessionStrategy to write the session id to the response and * persist the Session. */private void commitSession () {HttpSessionWrapper wrappedSession = getCurrentSession (); if (wrappedSession = null) {// session is invalid, delete cookie or header if (isInvalidateClientSession () {Se SsionRepositoryFilter. this. httpSessionStrategy. onInvalidateSession (this, this. response) ;}} else {S session = wrappedSession. getSession (); SessionRepositoryFilter. this. sessionRepository. save (session); if (! IsRequestedSessionIdValid () |! Session. getId (). equals (getRequestedSessionId () {// write the cookie or header back to the browser to save SessionRepositoryFilter. this. httpSessionStrategy. onNewSession (session, this, this. response) ;}}@ response ("unchecked") private HttpSessionWrapper getCurrentSession () {return (HttpSessionWrapper) getAttribute (response);} private void setCurrentSession (HttpSessionWrapper currentSession) {if (curren) TSession = null) {removeAttribute (CURRENT_SESSION_ATTR);} else {setAttribute (CURRENT_SESSION_ATTR, currentSession); }}@ SuppressWarnings ("unused") public String changeSessionId () {HttpSession session = getSession (false); if (session = null) {throw new IllegalStateException ("Cannot change session ID. there is no session associated with this request. ");} // eagerly get session attributes I N case implementation lazily loads them Map <String, Object> attrs = new HashMap <String, Object> (); Enumeration <String> iAttrNames = session. getAttributeNames (); while (iAttrNames. hasMoreElements () {String attrName = iAttrNames. nextElement (); Object value = session. getAttribute (attrName); attrs. put (attrName, value);} SessionRepositoryFilter. this. sessionRepository. delete (session. getId (); HttpSes SionWrapper original = getCurrentSession (); setCurrentSession (null); HttpSessionWrapper newSession = getSession (); original. setSession (newSession. getSession (); newSession. setMaxInactiveInterval (session. getMaxInactiveInterval (); for (Map. entry <String, Object> attr: attrs. entrySet () {String attrName = attr. getKey (); Object attrValue = attr. getValue (); newSession. setAttribute (attrName, attrValue );} Return newSession. getId () ;}// determine whether the session is valid @ Override public boolean isRequestedSessionIdValid () {if (this. requestedSessionIdValid = null) {String sessionId = getRequestedSessionId (); S session = sessionId = null? Null: getSession (sessionId); return isRequestedSessionIdValid (session);} return this. requestedSessionIdValid;} private boolean isRequestedSessionIdValid (S session) {if (this. requestedSessionIdValid = null) {this. requestedSessionIdValid = session! = Null;} return this. requestedSessionIdValid;} private boolean isInvalidateClientSession () {return getCurrentSession () = null & this. requestedSessionInvalidated;} private S getSession (String sessionId) {// obtain session S session = SessionRepositoryFilter from the session storage container Based on the sessionID. this. sessionRepository. getSession (sessionId); if (session = null) {return null;} // sets the last access time of sesison to prevent session expiration. SetLastAccessedTime (System. currentTimeMillis (); return session;}/** this method is not very familiar. The following is a more familiar getSession () method. Yes, it is here to re-obtain the session method **/@ Override public HttpSessionWrapper getSession (boolean create) {// get the session quickly, it can be understood that the relationship is HttpSessionWrapper currentSession = getCurrentSession (); if (currentSession! = Null) {return currentSession;} // obtain the sessionID String requestedSessionId = getRequestedSessionId () from httpSessionStratge Based on the cookie or header; if (requestedSessionId! = Null & getAttribute (INVALID_SESSION_ID_ATTR) = null) {// obtain the session from the storage container and set the current initialization attribute S session = getSession (requestedSessionId); if (session! = Null) {this. requestedSessionIdValid = true; currentSession = new HttpSessionWrapper (session, getServletContext (); currentSession. setNew (false); setCurrentSession (currentSession); return currentSession;} else {if (SESSION_LOGGER.isDebugEnabled () {SESSION_LOGGER.debug ("No session found by id: Caching result for getSession (false) for this HttpServletRequest. ");} setAttribute (INVALID_SESSION _ ID_ATTR, "true") ;}} if (! Create) {return null;} if (SESSION_LOGGER.isDebugEnabled () {SESSION_LOGGER.debug ("A new session was created. to help you troubleshoot where the session was created we provided a StackTrace (this is not an error ). you can prevent this from appearing by disabling DEBUG logging for "+ SESSION_LOGGER_NAME, new RuntimeException (" For debugging purposes only (not an error )"));} // If the browser or other http requests The first time the user accesses the server, a new session S session = SessionRepositoryFilter is created for the user. this. sessionRepository. createSession (); session. setLastAccessedTime (System. currentTimeMillis (); currentSession = new HttpSessionWrapper (session, getServletContext (); setCurrentSession (currentSession); return currentSession;} @ Override public ServletContext getServletContext () {if (this. servletContext! = Null) {return this. servletContext;} // Servlet 3.0 + return super. getServletContext () ;}@ Override public HttpSessionWrapper getSession () {return getSession (true) ;}@ Override public String getRequestedSessionId () {return SessionRepositoryFilter. this. httpSessionStrategy. getRequestedSessionId (this);}/** override class of HttpSession */private final class HttpSessionWrapper extends ExpiringSessionHttpSession <S> {HttpSessionWrapper (S session, ServletContext servletContext) {super (session, session, servletContext) ;}@ Override public void invalidate () {super. invalidate (); SessionRepositoryRequestWrapper. this. requestedSessionInvalidated = true; setCurrentSession (null); SessionRepositoryFilter. this. sessionRepository. delete (getId ());}}}}

Summary

The above is all the content about spring-session Introduction and implementation principle source code analysis in this article. I hope it will be helpful to you. If you are interested, you can continue to refer to other related topics on this site. If you have any shortcomings, please leave a message!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.