Know it, but also know why.
This article describes the principle of the entire implementation of spring-session. And the core of the source code for a simple introduction. Introduction to the principle of realization
Implementation principle here is a brief description:
When the Web server receives the HTTP request, and when the request enters the corresponding filter, the process that originally needs to be created by the Web server is forwarded to spring-session for creation, and the session that was created is saved in the Web server memory. Session information created through Spring-session can hold third-party services, such as Redis,mysql. Web servers share data by connecting to Third-party services to achieve session sharing.
the entire implementation process and source code detailed introduction
This source is based on the previous content, and in the save session will only analyze the use of jedisconnectionfactory implementation of the redisconnectionfactory. 1.SessionRepositoryFilter and jedisconnectionfactory registration process
Process:
Description
1. When starting a Web project, read Web.xml, read order Content-param--> Listener--> filter--> servlet
2. The role of the Contextloaderlistener listener is to automatically assemble the ApplicationContext configuration information
3 and initialize the root Web application context when the Web container is started.
4, springhttpsessionconfiguration registration Springsessionrepositoryfilter:bean,redishttpsessionconfiguration Registration Sessionredistemplate:bean and Sessionrepository:bean
5, configuration file configuration Jedisconnectionfactory implements Redisconnectionfactory, creating a jedisconnectionfactory bean
Code analysis is as follows: Web.xml, loading the XML configuration file and initializing the Web application context
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value> classpath*:spring/*xml</param-value>
</context-param>
<listener>
< Listener-class>org.springframework.web.context.contextloaderlistener</listener-class>
</ Listener>
In 2.application-session.xml, the Bean,web application of the redishttpsessionconfiguration bean and Jedisconnectionfactory is configured to initialize the load bean.
<!--Create a spring bean name Springsessionrepositoryfilter implementation filter.
the filter is responsible for replacing the HttpSession implementation with spring session support. In this instance, the spring session is supported by Redis. -->
<bean class= " Org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration "/>
< !--creates a redisconnectionfactory that connects the spring session to the Redis server. We configure the local host connected to the default port (6379). -->
<!--cluster redis-->
<bean id= "jedisconnectionfactory" class= " Org.springframework.data.redis.connection.jedis.JedisConnectionFactory ">
<!--redis-cluster-->
<constructor-arg index= "0" ref= "redisclusterconfig"/>
<!--Configure the Redis connection pool, you can not configure, use the default on the line. -->
<constructor-arg index= "1" ref= "Jedispoolconfig"/>
</bean>
Contextloaderlistener
/**
* Initializes the root Web application context.
*
/@Override public
void contextinitialized (Servletcontextevent event) {
Initwebapplicationcontext (Event.getservletcontext ());
}
4.RedisHttpSessionConfiguration class Diagram
Redishttpsessionconfiguration inherited Springhttpsessionconfiguration.
public class Redishttpsessionconfiguration extends Springhttpsessionconfiguration
implements Embeddedvalueresolveraware, Importaware {
4.1 springhttpsessionconfiguration to create a bean named Springsessionrepositoryfilter
@Bean public
<s extends expiringsession> sessionrepositoryfilter< extends expiringsession> Springsessionrepositoryfilter (
sessionrepository<s> sessionrepository) {
sessionrepositoryfilter <S> sessionrepositoryfilter = new sessionrepositoryfilter<s> (
sessionrepository);
Sessionrepositoryfilter.setservletcontext (this.servletcontext);
if (this.httpsessionstrategy instanceof multihttpsessionstrategy) {
Sessionrepositoryfilter.sethttpsessionstrategy (
(multihttpsessionstrategy) this.httpsessionstrategy);
}
else {
sessionrepositoryfilter.sethttpsessionstrategy (this.httpsessionstrategy);
}
return sessionrepositoryfilter;
}
4.2 Create a redishttpsessionconfiguration#redistemplate bean with the name Sessionredistemplate
@Bean public
Redistemplate<object, object> sessionredistemplate (
redisconnectionfactory ConnectionFactory) {
//instantiate redistemplate
redistemplate<object, object> template = new redistemplate< Object, object> ();
Sets the key serialization Stringredisserializer
Template.setkeyserializer (New Stringredisserializer ());
Set the hash key Stringredisserializer
Template.sethashkeyserializer (New Stringredisserializer ());
if (This.defaultredisserializer!= null) {
template.setdefaultserializer (this.defaultredisserializer);
}
Set ConnectionFactory. The fifth step is created (the actual connectionfactory loading process is not the same as the process sequence)
template.setconnectionfactory (connectionfactory);
return template;
}
4.3 Create a redishttpsessionconfiguration#redisoperationssessionrepository bean with the name Sessionrepository
@ Bean
Public RedisOperationsSessionRepository sessionRepository (
// use the sessionRedisTemplate bean
@ the Qualifier (" sessionRedisTemplate ") RedisOperations < Object, the Object > sessionRedisTemplate,
ApplicationEventPublisher ApplicationEventPublisher) {
/ / instantiate RedisOperationsSessionRepository
RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository (
SessionRedisTemplate);
/ / set applicationEventPublisher
SessionRepository. SetApplicationEventPublisher (applicationEventPublisher);
/ / set the maximum expiry time maxInactiveIntervalInSeconds = 1800
sessionRepository
SetDefaultMaxInactiveInterval (enclosing maxInactiveIntervalInSeconds);
If (this. DefaultRedisSerializer! = null) {
SessionRepository. SetDefaultSerializer (enclosing defaultRedisSerializer);
}
String redisNamespace = getRedisNamespace ();
If (StringUtils. HasText (redisNamespace)) {
SessionRepository. SetRedisKeyNamespace (redisNamespace);
}
SessionRepository. SetRedisFlushMode (enclosing redisFlushMode);
Return sessionRepository;
}
Create Redisconnectionfactory Bean as Jedisconnectionfactory
<bean id= "Jedisconnectionfactory" class= " Org.springframework.data.redis.connection.jedis.JedisConnectionFactory ">
2.SessionRepositoryFilter added to Filterchain
Process:
Description
1 2, in the Servlet3.0 specification, the servlet container launches automatically scans the Javax.servlet.ServletContainerInitializer implementation class, in which we can customize the classes that need to be loaded. The annotation @handlestypes (Webapplicationinitializer.class) allows the servlet container to automatically look for all Webapplicationinitializer implementation classes when the class is started.
2.1, the Insertsessionrepositoryfilter method obtains the Sessionrepositoryfilter through the FilterName, and creates the new Delegatingfilterproxy ( FilterName);
3 4, then add filter to Filterchain
1.ServletContainerInitializer implementation class loading and implementation of class loading via annotation @handlestypes (webapplicationinitializer.class)
Load implementation class
@HandlesTypes (Webapplicationinitializer.class)
// Springservletcontainerinitializer implementation Servletcontainerinitializer Public
class Springservletcontainerinitializer implements Servletcontainerinitializer {
//------------
2.AbstractHttpSessionApplicationInitializer implementation Webapplicationinitializer for loading
@Order public
Abstract class Abstracthttpsessionapplicationinitializer
implements Webapplicationinitializer {
2.1 Onstartup
public void Onstartup (ServletContext servletcontext) throws Servletexception {
Beforesessionrepositoryfilter ( ServletContext);
if (this.configurationclasses!= null) {
Annotationconfigwebapplicationcontext rootappcontext = new Annotationconfigwebapplicationcontext ();
Rootappcontext.register (this.configurationclasses);
Servletcontext.addlistener (New Contextloaderlistener (Rootappcontext));
}
Add Filter
insertsessionrepositoryfilter (ServletContext);
Aftersessionrepositoryfilter (ServletContext);
}
2.1.1.insertSessionRepositoryFilter
/**
* Registered springsessionrepositoryfilter
* @param servletcontext the {@link servletcontext} * *
Private void Insertsessionrepositoryfilter (ServletContext servletcontext) {
//Default_filter_name = " Springsessionrepositoryfilter "
String filtername = default_filter_name;
Create Delegatingfilterproxy delegatingfilterproxy through filtername
springsessionrepositoryfilter = new Delegatingfilterproxy (
filtername);
String ContextAttribute = Getwebapplicationcontextattribute ();
if (ContextAttribute!= null) {
springsessionrepositoryfilter.setcontextattribute (contextattribute);
}
Add filter to Filterchain
registerfilter (ServletContext, True, FilterName, based on filtername and context) Springsessionrepositoryfilter);
}
Registerfilter
private void Registerfilter (ServletContext ServletContext,
boolean insertbeforeotherfilters, String filtername, Filter Filter {
Dynamic registration = Servletcontext.addfilter (filtername, filter);
if (registration = = NULL) {
throw new IllegalStateException (
"Duplicate Filter registration for" + Filtername+ "'. Check to ensure the ' Filter is ' only configured once. ');
}
Whether asynchronous is supported, default True
registration.setasyncsupported (isasyncsessionsupported ());
Get Dispatchertype springsessionrepositoryfilter
enumset<dispatchertype> dispatchertypes = Getsessiondispatchertypes ();
Adds a filter map with the given URL pattern and the dispatcher type of the filter represented by this filterregistration. Filter mappings are matched in the order in which they are added.
registration.addmappingforurlpatterns (Dispatchertypes,!insertbeforeotherfilters,
"/*");
}
AddFilter Add filter to ServletContext
Public filterregistration.dynamic addfilter (
String FilterName, filter filter);
3.SessionRepositoryFilter interception Process
Process:
Description
1, request is delegatingfilterproxy: intercept to, and then execute the Dofilter method, in Dofilter find the execution of the proxy class.
2. Onceperrequestfilter: Agent Filter Executes Dofilter method, then calls abstract method Dofilterinternal
3, Sessionrepositoryfilter Inherits the Onceperrequestfilter, implements the Dofilterinternal, this method encapsulates a Wrappedrequest, saves session information by executing commitsession to Redis
1 request came in, intercepted by Delegatingfilterproxy, configured in Web.xml
1.1 Executive Dofilter
If you do not specify a destination bean name, use the filter name.
@Override public void Dofilter (ServletRequest request, servletresponse response, Filterchain Filterchain) Throws Servletexception, IOException {//if required, delay initialization of delegates.
necessary.
Filter delegatetouse = this.delegate; if (Delegatetouse = = null) {synchronized (this.delegatemonitor) {if (this.delegate = = null)
{Webapplicationcontext WAC = Findwebapplicationcontext ();
if (WAC = = null) {throw new IllegalStateException ("No Webapplicationcontext found:" +
"No Contextloaderlistener or Dispatcherservlet registered");
} this.delegate = Initdelegate (WAC);
} delegatetouse = This.delegate;
}//Let the delegate perform the actual dofilter operation Invokedelegate (Delegatetouse, request, response, Filterchain); }
1.2 Initdelegate
Protected Filter initdelegate (Webapplicationcontext WAC) throws Servletexception {
// You can get to sessionrepositoryfilter [remark 1]
Filter delegate = Wac.getbean (Gettargetbeanname (), filter.class);
if (Istargetfilterlifecycle ()) {
delegate.init (Getfilterconfig ());
}
return delegate;
}
[Note 1] because: Sessionrepositoryfilter is one of the highest priority Javax.servlet.Filter
/* @Order ( Sessionrepositoryfilter.default_order) Public
class Sessionrepositoryfilter<s extends expiringsession>
extends Onceperrequestfilter {
* * *
Delegate.dofilter ();
protected void Invokedelegate (
Filter delegate, ServletRequest request, servletresponse response, Filterchain Filterchain)
throws Servletexception, IOException {
//Agent to execute Dofilter, agent for Sessionrepositoryfilter
Delegate.dofilter (Request, response, filterchain);
}
2.1 Onceperrequestfilter#dofilter
Public final void Dofilter (ServletRequest request, servletresponse response, Filterchain Filterchain) throws S Ervletexception, IOException {if (!) ( Request instanceof HttpServletRequest) | | ! (Response instanceof HttpServletResponse))
{throw new Servletexception ("Onceperrequestfilter just supports HTTP requests");
} httpservletrequest HttpRequest = (httpservletrequest) request;
HttpServletResponse HttpResponse = (httpservletresponse) response;
Boolean hasalreadyfilteredattribute = Request. getattribute (this.alreadyfilteredattributename)!= null; if (Hasalreadyfilteredattribute) {//without calling this filter ... filterchain.dofilter (Request, Respon
SE); else {//call this filter ... request.setattribute (This.alreadyfilteredattributename, Boolean.true)
;
Try {//dofilterinternal is an abstract methodDofilterinternal (HttpRequest, HttpResponse, Filterchain);
Finally {//delete "filtered" Request properties for this request.)
Request.removeattribute (This.alreadyfilteredattributename);
}
}
}
Execute sessionrepositoryfilter#dofilterinternal
@Override protected void dofilterinternal (HttpServletRequest request, httpservletresponse response, Filter Chain Filterchain) throws Servletexception, IOException {request.setattribute (session_repository_attr
, this.sessionrepository); Create a sessionrepositoryrequestwrapper using HttpServletRequest, HttpServletResponse, and ServletContext SessionRepositoryR Equestwrapper wrappedrequest = new Sessionrepositoryrequestwrapper (request, Response, This.servletcontext
); Sessionrepositoryresponsewrapper wrappedresponse = new Sessionrepositoryresponsewrapper (WrappedRequest, R
Esponse); Use Cookiehttpsessionstrategy to repackage httpservletrequest httpservletrequest strategyrequest = This.httpSessionStrateg
Y. Wraprequest (Wrappedrequest, wrappedresponse); HttpServletResponse strategyresponse = this.httpsessionstrategy. Wrapresponse (Wrappedrequest, WrappedRespo
NSE); TrY {//Execute other filter filterchain.dofilter (Strategyrequest, strategyresponse);
Finally {//Save session Information Wrappedrequest.commitsession ();
}
}
4. Wrappedrequest.commitsession () Look at the fourth big Point analysis 4.SessionRepository save session data
Process:
Description
1, submit session Save
2, get the current session, this step is more important, get a httpsessionwrapper, this httpsessionwrapper replaced HttpSession
3, Wrappedsession gets the current session
4, saves session content using Redistemplate, and invokes Redisconnection Use its implementation class jedisclusterconnection to get Redis connections
1.commitSession
/**
* Writes the session ID to the response using Httpsessionstrategy. * Save session.
* *
private void Commitsession () {
Httpsessionwrapper wrappedsession = getcurrentsession ();
if (wrappedsession = = null) {
if (isinvalidateclientsession ()) {
SessionRepositoryFilter.this.httpSessionStrategy
. Oninvalidatesession (this, this.response);
}
}
else {
S session = Wrappedsession.getsession ();
SessionRepositoryFilter.this.sessionRepository.save (session);
if (!isrequestedsessionidvalid ()
| | |!session.getid (). Equals (Getrequestedsessionid ())) {
SessionRepositoryFilter.this.httpSessionStrategy.onNewSession (Session, this
, this.response);}}
2.getCurrentSession
Session Repository Request property name.
public static final String session_repository_attr = Sessionrepository.class. GetName (); private static final String current_session_attr = session_repository_attr + ".
Current_session ";
Private Httpsessionwrapper getcurrentsession () {return (Httpsessionwrapper)//Get session
GetAttribute (CURRENT_SESSION_ATTR);
/** * The default behavior of this method is to invoke GetAttribute (string name) on the wrapper request object.
*/Public Object getattribute (String name) {///The request here is the return This.request.getAttribute (name) encapsulated above; }<