1: Введение в весеннюю сессию
1. Введение
Сессия всегда была сложной проблемой, которую нам нужно решить при выполнении кластеров. В прошлом мы могли решить его из контейнеров для сервлетов, таких как Tomcat-Redis-Session-Manager и Memcached-Session-Manager, предоставляемый контейнерным контейнером с открытым исходным кодом.
Или IP_HASH с помощью балансировки нагрузки, такого как Nginx и маршрутизацию на конкретный сервер.
Но оба метода имеют недостатки.
Весенняя сессия-это проект под весной. Он заменяет httpsession, реализованный контейнером сервлета, на пружине, сосредоточившись на решении задач управления сеансами. Это может быть интегрировано в наши приложения быстро и плавно.
2. Функции поддержки
1) Легко хранить сеансы в сторонних контейнерах для хранения. Структура предоставляет Redis, JVM Map, Mongo, Gemfire, Hazelcast, JDBC и другие контейнеры для хранения сеансов.
2) Один и тот же браузер и один и тот же веб -сайт поддерживают несколько задач сеанса.
3) RESTFULAPI, не полагается на печенье. JessionId можно пройти через заголовок
4) Объединение WebSocket и Spring-Session для синхронизации управления жизненным циклом.
3. Метод интеграции
Метод интеграции очень прост, просто посмотрите на образцы и руководство на официальном сайте. http://docs.spring.io/spring-session/docs/1.3.0.release/reference/html5/
Он в основном разделен на следующие этапы интеграции:
1) Внедрить пакет JAR -зависимости
2) Метод аннотации или метод XML для настройки методов хранения для конкретных контейнеров для хранения, таких как метод конфигурации Redis XML
<Контекст: Annotation-config/>/** Инициализировать все препараты для пружинного сеанса и поместить SpringsessionFilter в ioc **/<beanclass = "org.springframework.session.data.redis.config.annotation.web.http.redishtpsessionConfiguration"/*** Это конфликт для ла <beanclass = "org.springframework.data.redis.connection.lettuce.lettuceConnectionFactory"/>
3) Настройка web.xml в методе XML, настройте SpringsessionFilter в цепочку фильтров
<Filter> <Filter-name> SpringSessionRepositoryFilter </filter-name> <filter-class> org.springframework.web.filter.delegatingfilterproxy </filter-class> </filter> <urpation> <filter-mame> springsessionRepositoryFilter </filter> <UR-pattern> <urll-pattern> <ур-pattern-pattern> <ur <Диспетчер> запрос </dispatcher> <Диспетчер> Ошибка </dispatcher> </filter-mapping>
Два: внутренний анализ структуры пружинного сессии
1. Схема абстрактной структуры на высоком уровне рамки
2. Сессионная сессия переписывает запрос сервлета и проблемы, связанные с хранением Redis
Общий принцип пружинного сеанса плавно заменяет запрос сервера приложений:
1. Настройте фильтр и реализуйте метод Dofilter
2. Унаследовать классы httpservlectrequestwrapper и httpservletresponsewrapper и переопределить Getsession и другие связанные методы (в этих методах вызывается соответствующий класс контейнера для хранения сеансов).
3. На шаге Dofilter на первом шаге новая класса запроса и ответа на втором этапе. и передать их отдельно к цепочке фильтров
4. Настройте фильтр в первую позицию цепочки фильтра
/** Этот класс является исходным кодом пружины 1.30, и это также ключевой класс, который реализует первые до третьих шагов выше **/открытый класс SessionRepositoryFilter <S Extends DepiringSessessessessession> ExtencePerrequestFilter {/** Интерфейс хранения сеанса/Private SessionRepors> MongoDB, GenFire и другие DataBases All Interface **/Private PostortPorporps> Sessitory); Private ServletContext ServletContext; /** Интерфейс доставки SessionId. В настоящее время Spring-Session поставляется с двумя классами реализации 1. Куки-метод: cookiehttpsessionstrategy 2.http Метод заголовка: Headerhttpsessionstrategy Конечно, мы также можем настроить другие методы. **/ private multihtpsessionstrategy httpsessionstrategy = new cookiehttpsessionstrategy (); public sessionRepositoryfilter (sessionRepository <s> sessionRepository) {if (sessionRepository == null) {бросить новый allosalargumentException («SessionRepository не может быть нулевым»); } this.sessionRepository = sessionRepository; } public void sethttpsessionStrategy (httpsessionstrategy httpsessionstrategy) {if (httpsessionstrategy == null) {бросить новый allosalargumentException («httpsessionstrategy не может быть нулевым»); } /** Благодаря предыдущему введению функции пружины мы знаем, что пружина может поддерживать несколько сеансов в одном браузере, что достигается через MultiHtpsessionStrategyDapter. У каждого браузера есть SessionID, но этот SessionID имеет несколько псевдонимов (в соответствии с вкладкой браузера). Например: псевдоним 1 SessionId Alias 2 SessionId ... и этот псевдоним передается через URL, который является принципом нескольких сеансов одним браузером **/ this.httpsessionStrategy = новый мультигтпсессий } public void sethttpsessionStrategy (multihtpsessionstrategy httpsessionstrategy) {if (httpsessionstrategy == null) {бросить новый allosalargumentException («httpsessionstrategy не может быть нулевым»); } this.httpsessionStrategy = httpsessionstrategy; } /** Этот метод эквивалентен переписыванию Дофильтера, но пружина имеет еще один слой инкапсуляции. Создайте пользовательский запрос и ответ в этом методе, а затем передайте его в цепочку фильтров FilterChain **/ @Override Protected void Dofilterinternal (httpservletrequest, httpservletresponse, Filterchain filterchain) Throws_attrestory, ioException {request.setTtribute (session_repositorition_attrestory, this.semessition_attrestorietrytrysition_attrestorietry. /** ServletRequest переписывается весной-сессией. Этот класс наследует httpservlectrequestwrapper **/ sessionRepositoryRequestwrapper wrappedRequest = new SessionRepositoryRequestWrapper (запрос, ответ, this.servletContext); SessionRepositoryResponseWrapper TradResponse = New SessionRepositoryResponseWrapper (TraveRrequest, ответ); Httpservlectrequest regnegyRequest = this.httpsessionstrategy.wraprequest (wrappedRequest, wrappedResponse); Httpservletresponse restressonse = this.httpsessionstrategy .wrapresponse (wrappedRequest, wrappedResponse); Попробуйте { /** передать пользовательский запрос и ответ в цепочку. Представьте себе, если Spring-SessionFilter расположен в первой из цепочки фильтров, то есть ли последующие фильтры, а также запрос и ответ, полученные путем достижения последнего уровня управления, мы настраиваем его? **/ filterChain.dofilter (стратегия, стратегия, стратегия); } наконец {wrappedRequest.commitsession (); }} public void setServletContext (ServletContext servletContext) {this.servletContext = ServletContext; } / ** Это переписанный класс ответа сервлета* / private final Class SessionRepositoryResponseWrapper Extends OncommittedResponseWrapper {private final SessionRepositoryRequestWrapper запрос; SessionRepositoryResponseWrapper (SessionRepositoryRequestWrapper запрос, httpservletresponse response) {super (response); if (request == null) {бросить новый allodalargumentException («запрос не может быть null»); } this.request = request; } /** Этот шаг состоит в том, чтобы сохранить сеанс в контейнер для хранения. Мы можем позвонить в метод работы сеанса несколько раз в управляющем уровне. Если каждая операция сеанса сохраняется в контейнере для хранения, это определенно окажет влияние на производительность. Например, Redis, поэтому мы можем выполнить весь элемент управления, и ответ возвращает информацию в браузер, и сеанс будет сохраняться только тогда, когда ответ возвращает информацию в браузер. **/ @Override Protected void onResponseCommated () {this.request.commitsession (); }} /** Класс переписывания запроса пружины является почти самым важным классом переписывания. Он переписал такие методы, как Getsession, Session и другие методы, а также классы */ Private Final Class SessionRepositoryRequestwrapper Extends httpservlectrequestwrapper {Private Boolean запрашиваемое значение. Частный логический запрос о просьбе; Частный окончательный ответ httpservletresponse; Частный финальный сервисконтекст ServletContext; Private SessionRepositoryRequestWrapper (запрос httpservlectrequest, httpservletresponse, ответ ServletContext revletContext) {super (запрос); this.Response = ответ; this.servletContext = ServletContext; } /** * Использует httpsessionStrategy, чтобы написать идентификатор сеанса в ответ и * сохранить сеанс. */ private void commitsession () {httpsessionWrapper wrupsession = getCurrentSession (); if (wrappedsession == null) {// сеанс истекает, удаляет файлы cookie или заголовок if (isInvalidateClientsession ()) {sessionRepositoryfilter.hits.httpsessionStrategy .oninvalidatesession (this, this.те.sresponse); }} else {s session = wrupsession.getSession (); SessionRepositoryfilter.this.sessionRepository.save (Session); if (! IsrequestSessionIdvalid () ||! Session.getId (). equals (getRequestSessionId ())) {// Напишите cookie или заголовок обратно в браузер, чтобы сохранить SessionRepositoryfilter.his.httpsessionStrategy.onnewsession (SessionE, This.Response); }}} @Suppresswarnings ("unchecked") private httpsessionWrapper getCurrentSessionSession () {return (httpsessionWrapper) getAttribute (current_session_attr); } private void setcurrentSession (httpsessionWrapper currentsession) {if (currentsession == null) {removeAttribute (current_session_attr); } else {setAttribute (current_session_attr, currentsession); }} @Suppresswarnings ("unared") public string informessessionId () {httpsession session = getsession (false); if (session == null) {бросить новое нелегальное организация («не может изменить идентификатор сеанса. Сессия не связана с этим запросом.»); } // с нетерпением получить атрибуты сеанса в случае реализации ленивый загружает их карту <строка, объект> attrs = new hashmap <string, object> (); Enumeration <string> iattrnames = session.getattributeNames (); while (iattrnames.hasmoreElements ()) {string attrname = iattrnames.nextelement (); Значение объекта = session.getAttribute (attrname); attrs.put (attrname, значение); } SessionRepositoryfilter.this.sessionRepository.delete (session.getid ()); HttpsessionWrapper original = getCurrentSession (); SetCurrentSession (NULL); Httpsessionwrapper newsession = getsession (); Original.setsession (newsession.getSession ()); newsession.setMaxInativeInterval (session.getMaxInativeInterVal ()); for (map.Entry <string, object> attr: attrs.entryset ()) {string attrname = attr.getkey (); Object attrvalue = attr.getValue (); newsession.setattribute (attrname, attrvalue); } return newsession.getId (); } // Определите, является ли сеанс действительным @Override public boolean isrequestSessionSessionIdvalid () {if (this.requestSessionSidyDvalid == null) {String sessionId = getRequestessessionId (); S session = sessionId == null? null: getsession (sessionId); возврат isrequestSessionIdvalid (сессия); } вернуть это. } Частный логический IsrequestSessionIdvalid (s Session) {if (this.RequestSessionSidValid == null) {this.RectedSessionIdvalid = session! = null; } вернуть это. } Частный логический iSInvalidateClientsession () {return getCurrentSession () == null && this.cessessionSessionInvalidated; } private s getSession (String SessionId) {// Get Session из контейнера для хранения сеанса на основе SessionId S SESSION = SESSIONREPOSITIONFILTER.THIS.SessionRepository .getSession (sessionId); if (session == null) {return null; } // Установите последнее время доступа сеанса, чтобы предотвратить сессию сеанса с истекшим сессией. SetlastaccessedTime (System.currentTimeMillis ()); возвратный сеанс; } /** Этот метод очень знаком? Есть также getsession () ниже, чтобы быть более знакомым. Правильно, просто повторно поместите метод сеанса здесь **/@Override public httpsessionwrapper getsession (boolean create) {// быстро получить сеанс, который можно понимать как отношения между кешем первого уровня и кэшем второго уровня httpsessionwrapper currentsession = getcurrentsession (); if (Currentsession! = null) {return currentsession; } // Получить SessionId от httpsessionstret reashedSessionId = getRequestSessionId (); if (запрошенная сторона! = null && getAttribute (vangiad_session_id_attr) == null) {// получить сеанс из контейнера для хранения и установить текущий атрибут инициализации session = getsession (запрос sessionidid); if (session! = null) {this.requedSessionIdvalid = true; Currentsession = новый httpsessionWrapper (Session, getServletContext ()); CurrentSession.SetNew (false); Setcurrentsession (тока); возвращение токи; } else {if (session_logger.isdebugenabled ()) {session_logger.debug ("Нет сеанса, не найденный идентификатором: результат кэширования для getsession (false) для этого httpservletrequest."); } setAttribute (Invalid_session_id_attr, "true"); }} if (! create) {return null; } if (session_logger.isdebugenabled ()) {session_logger.debug («Новый сеанс был создан. Чтобы помочь вам устранение неполадок, в которой был создан сеанс, мы предоставили stacktrace (это не ошибка). Вы можете помешать этому появлению, отключив журнал дола для« cheess_logger_name, new Runtimexcection »(net unmerposes nute warposess nute warposess nute warposess nute an trueposessesses in net ane warposses nute warposses nute warposess sevicger_name. } // Если браузер или другой посетитель HTTP впервые обращаются к серверу, создайте новое сеанс S для него = SessionRepositoryfilter.this.sessionRepository.createsession (); Session.SetLastAccedTime (System.CurrentTimeMillis ()); Currentsession = новый httpsessionWrapper (Session, getServletContext ()); Setcurrentsession (тока); возвращение токи; } @Override public servletContext getServletContext () {if (this.servletContext! = Null) {return this.servletcontext; } // Сервлет 3.0+ return super.getServletContext (); } @Override public httpsessionWrapper getSession () {return getSession (true); } @Override public String getRequestSessionId () {return SessionRepositoryfilter.this.httpsessionstrategy .getRequestedSessionId (this); } / ** Переписать класс httpsession* / private final Class httpsessionWrapper Extends expiringSessionHttpsession <s> {httpsessionWrapper (s Session, ServletContext ServletContext) {Super (Session, ServletContext); } @Override public void Invalidate () {super.invalidate (); SessionRepositoryRequestWrapper.This. SetCurrentSession (NULL); SessionRepositoryfilter.this.sessionRepository.delete (getId ()); }}}}Суммировать
Выше приведено все содержание этой статьи о внедрении пружинного сеанса и анализе исходного кода принципов реализации. Я надеюсь, что это будет полезно для всех. Заинтересованные друзья могут продолжать ссылаться на другие связанные темы на этом сайте. Если есть какие -либо недостатки, пожалуйста, оставьте сообщение, чтобы указать это!