мы знаем,HttpServletRequset
иHttpServletResponse
даServlet
указанный стандартомJava
язык иWeb
Интерфейс для взаимодействия с контейнером. Сам интерфейс указывает толькоjava
языковая параweb
Поведение контейнера при доступе и конкретная реализация определяются разнымиweb
Контейнер реализован внутри него.
Затем во время выполнения, когда нам нужноHttpServletRequset
иHttpServletResponse
При расширении экземпляра по умолчанию мы можем наследоватьHttpServletRequestWrapper
иHttpServletResponseWrapper
реализовать.
существуетSpringSession
потому что мы хотим реализовать контейнер, который не зависит от самого контейнераgetSession
реализации, поэтому его необходимо расширитьHttpServletRequset
, переписавgetSession
для достижения распределенногоsession
Способность. Давайте посмотрим нижеSpringSession
среда дляHttpServletRequset
расширение.
1. Запросить перезапись
SpringSession
Для перезаписи запросов способность в основном отражается в аспекте хранения, то естьgetSession
метод. существуетSessionRepositoryFilter
В этом классе реализован внутренний класс для реализацииHttpServletRequset
иHttpServletResponse
расширение.
1.1 Реализация расширения HttpServletRequset
private final class SessionRepositoryRequestWrapper
extends HttpServletRequestWrapper {
// HttpServletResponse 实例
private final HttpServletResponse response;
// ServletContext 实例
private final ServletContext servletContext;
// requestedSession session对象
private S requestedSession;
// 是否缓存 session
private boolean requestedSessionCached;
// sessionId
private String requestedSessionId;
// sessionId 是否有效
private Boolean requestedSessionIdValid;
// sessionId 是否失效
private boolean requestedSessionInvalidated;
// 省略方法
}
1.2 Конструктор
private SessionRepositoryRequestWrapper(HttpServletRequest request,
HttpServletResponse response, ServletContext servletContext) {
super(request);
this.response = response;
this.servletContext = servletContext;
}
Конструктор будетHttpServletRequest
,HttpServletResponse
а такжеServletContext
Экземпляр передается для использования последующими расширениями.
1.3 Метод getSession
@Override
public HttpSessionWrapper getSession(boolean create) {
// 从当前请求线程中获取 session
HttpSessionWrapper currentSession = getCurrentSession();
// 如果有直接返回
if (currentSession != null) {
return currentSession;
}
// 从请求中获取 session,这里面会涉及到从缓存中拿session的过程
S requestedSession = getRequestedSession();
if (requestedSession != null) {
// 无效的会话id(不支持的会话存储库)请求属性名称。
// 这里看下当前的sessionId是否有效
if (getAttribute(INVALID_SESSION_ID_ATTR) == null) {
// 设置当前session的最后访问时间,用于延迟session的有效期
requestedSession.setLastAccessedTime(Instant.now());
// 将requestedSessionIdValid置为true
this.requestedSessionIdValid = true;
// 包装session
currentSession = new HttpSessionWrapper(requestedSession, getServletContext());
// 不是新的session,如果是新的session则需要改变sessionId
currentSession.setNew(false);
// 将session设置到当前请求上下文
setCurrentSession(currentSession);
// 返回session
return currentSession;
}
}
else {
// 这里处理的是无效的sessionId的情况,但是当前请求线程 session有效
if (SESSION_LOGGER.isDebugEnabled()) {
SESSION_LOGGER.debug(
"No session found by id: Caching result for getSession(false) for this HttpServletRequest.");
}
// 将invalidSessionId置为true
setAttribute(INVALID_SESSION_ID_ATTR, "true");
}
// 是否需要创建新的session
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)"));
}
// 创建新的session
S session = SessionRepositoryFilter.this.sessionRepository.createSession();
// 设置最后访问时间,也就是指定了当前session的有效期限
session.setLastAccessedTime(Instant.now());
// 包装下当前session
currentSession = new HttpSessionWrapper(session, getServletContext());
//设置到当前请求线程
setCurrentSession(currentSession);
return currentSession;
}
В приведенном выше коде есть несколько моментов, которые поясняются здесь отдельно.
-
getCurrentSession
- Это для того, чтобы избежать повторного получения сессии из хранилища во время одного и того же процесса запроса.При поступлении новой установите текущую сессию на текущий запрос.Если вам нужен getSession в последующем процессе обработки, вам не нужно его хранить Снова возьми его в среду.
-
getRequestedSession
- Это основано на запрошенной информации
session
, которая включает в себяsessionId
Разобрать, получить из хранилищаsession
объекты и т.д.
- Это основано на запрошенной информации
- создавать ли новые
session
объект- Ни в текущем запросе, ни в хранилище
session
информация, здесь будет основываться наcreate
параметр, определяющий, следует ли создавать новыйsession
. Здесь обычный пользователь входит в систему в первый раз илиsession
Пойдет, когда не получится.
- Ни в текущем запросе, ни в хранилище
1.4 getRequestedSession
Получено из запрошенной информацииsession
объект
private S getRequestedSession() {
// 缓存的请求session是否存在
if (!this.requestedSessionCached) {
// 获取 sessionId
List<String> sessionIds = SessionRepositoryFilter.this.httpSessionIdResolver
.resolveSessionIds(this);
// 通过sessionId来从存储中获取session
for (String sessionId : sessionIds) {
if (this.requestedSessionId == null) {
this.requestedSessionId = sessionId;
}
S session = SessionRepositoryFilter.this.sessionRepository
.findById(sessionId);
if (session != null) {
this.requestedSession = session;
this.requestedSessionId = sessionId;
break;
}
}
this.requestedSessionCached = true;
}
return this.requestedSession;
}
Этот код все еще очень интересен, возьмите его здесьsessionId
Что возвращается, так это список. Конечно вотSpringSession
стратегии внедрения, потому что поддержкаsession
, поэтому здесь он возвращается в виде списка. ОК, продолжайте смотреть, как анализироватьsessionId
из:
можно увидеть здесьSpringSession
заsessionId
Две стратегии приобретения, одна основана наcookie
, один основан наheader
; Конкретную реализацию смотрите отдельно.
1.4.1 CookieHttpSessionIdResolver получает идентификатор сеанса
CookieHttpSessionIdResolver
получено вsessionId
Основной код выглядит следующим образом:
cookie
. отrequest
будетcookie
Выньте информацию, а затем пройдите, чтобы найти текущуюsessionId
соответствующийcookie
, здесь суждение тоже очень простое, если даSESSION
начало, значитSessionId
,после всегоcookie
делится не толькоsessionId
и, возможно, другой контент.
Кроме того, есть jvmRoute, который редко используется, потому что в большинстве случаев это значение равно null. Это мы анализируемCookieSerializer
объясню позже.
1.4.2 HeaderHttpSessionIdResolver для получения идентификатора сеанса
Это приобретение более прямое и грубое, основанное наheaderName
отheader
значение в.
назадgetRequestedSession
, ядро остальной части кода иsessionRepository
Это связано, и эта часть будет включать в себя часть хранения. Это выходит за рамки анализа этой статьи и будет проанализировано в части реализации хранилища.
1.5 HttpSessionWrapper
В приведенном выше коде, когда мы получаемsession
Экземпляр обычно упакован, поэтому используетсяHttpSessionWrapper
.
HttpSessionWrapper
наследоватьHttpSessionAdapter
,этоHttpSessionAdapter
заключается в преобразовании SpringSession в стандартныйHttpSession
Класс адаптера.HttpSessionAdapter
стандартныйservlet
нормативныйHttpSession
интерфейс.
1.5.1 HttpSessionWrapper
HttpSessionWrapper
переписанныйinvalidate
метод. Из кода эффект вызова этого метода:
-
requestedSessionInvalidated
установлен вtrue
, который определяет текущийsession
неверный. - в текущем запросе
session
Установить какnull
, то при последующих обращениях к запросу черезgetCurrentSession
не получитsession
Информация. - Текущая кэшированная сессия очищена, включая sessionId, экземпляр сессии и т. д.
- Удалите объект сеанса на носителе данных.
1.5.2 HttpSessionAdapter
SpringSession
и стандартныйHttpSession
класс конфигуратора. Как это понять, посмотрите на следующий кусок кода:
@Override
public Object getAttribute(String name) {
checkState();
return this.session.getAttribute(name);
}
Для реализаций на основе самого контейнераHttpSession
Сказать,getAttribute
Реализация также определяется самим контейнером. Но после преобразования здесь,getAttribute
пройдетSpringSession
получить решение, реализованное в. другиеAPI
Адаптация также основана на этой реализации.
SessionCommittingRequestDispatcher
ДостигнутоRequestDispatcher
интерфейс. оRequestDispatcher
Вы можете обратиться к этой статье[Сервлет] Принцип работы RequestDispatcher.SessionCommittingRequestDispatcher
правильноforward
поведение не изменилось.
заinclude
вinclude
представлено доsession
. Зачем это делать?
так какinclude
способ сделать оригиналServlet
и отправленоServlet
может выводить ответную информацию, то есть оригиналServlet
Вы также можете продолжать выводить ответную информацию, т. е. после того, как запрос будет переадресован, исходныйServlet
Вы также можете продолжать выводить ответную информацию, которая пересылаетсяServlet
Ответ на запрос будет объединен с исходнымServlet
в объекте ответа.
Так что этоinclude
позвонить перед звонкомcommit
, что гарантирует, что включенныйServlet
Программа не может изменить код состояния и заголовки ответа ответного сообщения.
2 Ответ переписать
Цель перезаписи ответа — обеспечить возможность сохранения сеанса при отправке запроса. посмотриSessionRepositoryResponseWrapper
Реализация класса:
onResponseCommitted
, то есть, как упоминалось выше, при отправке запроса эта функция обратного вызова может быть использована дляsession
Сохраните в контейнер для хранения.
2.1 представление сеанса
Наконец, взгляните на commitSession
Этот процесс больше не пойдет в контейнер храненияsession
информации, а непосредственно из текущего запроса. Если не получится, напишитеcookie
изменит текущийsession
соответствующийcookie
Значение установлено пустым, чтобы оно было перенесено при поступлении следующего запроса.sessionCookie
пуст, что приведет к повторному входу в систему.
Если получено, очистить текущий запросsession
информацию, а затемsession
в емкость для хранения иsessionId
написать ответcookie
середина.
резюме
Эта статья в основном посвященаSpringSession
переписать вRequest
иResponse
Проанализировано. путем переписыванияRequest
просьба прийтиsession
Хранилище связано с контейнером хранилища путем переопределенияResponse
иметь дело сsession
представить, будетsession
Сохраните в контейнер для хранения.
Позже мы продолжим анализSpringSession
исходный код. Я также недавно узнаю о технологиях отслеживания ссылок, и я собираюсь написать об этом, заинтересованные студенты могут обсудить это вместе.