// 《Tomcat8.x Servlet应用 Session的机制 》
// ----------------------------------
// 创建Session管理器,Session的初始化 -------------1
// ----------------------------------
class org.apache.catalina.core.StandardContext{
protected synchronized void startInternal() throws LifecycleException {
// Acquire clustered manager
Manager contextManager = null;
Manager manager = getManager();
if (manager == null) {
if ( (getCluster() != null) && distributable) { // 如果有配置集群/并且启动了分布式
try {
contextManager = getCluster().createManager(getName());
} catch (Exception ex) {
log.error("standardContext.clusterFail", ex);
ok = false;
}
} else {
contextManager = new StandardManager(); // 上下文管理器,Session管理器
}
}
// Configure default manager if none was specified
if (contextManager != null) {
setManager(contextManager); // 设置上下文管理器,Session管理器
}
// Start manager
Manager manager = getManager();
if (manager instanceof Lifecycle) {
// org.apache.catalina.session.StandardManager
((Lifecycle) manager).start(); // 启动上下文管理器,即启动Session管理器,加载服务器上的Session文件
}
}
}
// Session管理器
class org.apache.catalina.session.StandardManager{
protected synchronized void startInternal() throws LifecycleException {
super.startInternal();
// Load unloaded sessions, if any
load(); // 加载服务器上的Session文件
setState(LifecycleState.STARTING);
}
// 加载服务器上的Session文件
public void load() throws ClassNotFoundException, IOException {
doLoad(); // 加载服务器上的Session文件
}
// 加载服务器上的Session文件
protected void doLoad() throws ClassNotFoundException, IOException {
// Initialize our internal data structures
sessions.clear();
// Open an input stream to the specified pathname, if any
File file = file();//!!! 在临时目录中读取Session信息
if (file == null) {
return;
}
Loader loader = null;
ClassLoader classLoader = null;
Log logger = null;
try (FileInputStream fis = new FileInputStream(file.getAbsolutePath());
BufferedInputStream bis = new BufferedInputStream(fis)) {
// org.apache.catalina.core.StandardContext
Context c = getContext();
loader = c.getLoader();
logger = c.getLogger();
if (loader != null) {
classLoader = loader.getClassLoader();
}
if (classLoader == null) {
classLoader = getClass().getClassLoader();
}
// Load the previously unloaded active sessions
synchronized (sessions) { // 解析Session文件内容
try (ObjectInputStream ois = new CustomObjectInputStream(bis, classLoader, logger,
getSessionAttributeValueClassNamePattern(),
getWarnOnSessionAttributeFilterFailure())) { // 读取d:/a/b/tomcat8.x/work/Catalina/localhost/test/SESSIONS.ser
Integer count = (Integer) ois.readObject(); // 读取Session对象
int n = count.intValue();
for (int i = 0; i < n; i++) {
// org.apache.catalina.session.StandardSession
StandardSession session = getNewSession(); // 创建StandardSession
session.readObjectData(ois); // 解析Session文件
session.setManager(this);
sessions.put(session.getIdInternal(), session);
session.activate();
if (!session.isValidInternal()) { // 检查是否失效
// If session is already invalid,
// expire session to prevent memory leak.
session.setValid(true);
session.expire();
}
sessionCounter++;
}
} finally {
// Delete the persistent storage file
if (file.exists()) {
file.delete();
}
}
}
}
}
// 查找Session文件
protected File file() {
if (pathname == null || pathname.length() == 0) {
return null;
}
File file = new File(pathname);// SESSIONS.ser
if (!file.isAbsolute()) {
// context === org.apache.catalina.core.StandardContext
Context context = getContext();
ServletContext servletContext = context.getServletContext();
File tempdir = (File) servletContext.getAttribute(ServletContext.TEMPDIR);
if (tempdir != null) {
file = new File(tempdir, pathname); // 从临时目录中加载Session文件
}
}
return file;
}
}
// ----------------------------------
// Session 的调用 -------------2
// ----------------------------------
HttpSession httpSession = req.getSession();
httpSession.setAttribute("userInfo", "userInfo_Value");
httpSession.getAttribute("userInfo");
class org.apache.catalina.connector.RequestFacade{
public HttpSession getSession() {
return getSession(true);
}
public HttpSession getSession(boolean create) {
return request.getSession(create);
}
}
class org.apache.catalina.connector.Request{
public HttpSession getSession(boolean create) {
Session session = doGetSession(create);//!!!
if (session == null) {
return null;
}
return session.getSession();
}
protected Session doGetSession(boolean create) {
// There cannot be a session if no context has been assigned yet
Context context = getContext(); // org.apache.catalina.core.StandardContext
if (context == null) {
return (null);
}
// Return the current session if it exists and is valid
if ((session != null) && !session.isValid()) { // 如果Session已经过期,从session管理中删除本对象,就返回空
session = null;
}
if (session != null) {
return (session);
}
// Return the requested session if it exists and is valid
// org.apache.catalina.session.StandardManager
Manager manager = context.getManager();
if (manager == null) {
return (null); // Sessions are not supported
}
if (requestedSessionId != null) {
try {
// manager === org.apache.catalina.session.StandardManager
//查找SessionID是否已经存在
session = manager.findSession(requestedSessionId);
} catch (IOException e) {
session = null;
}
if ((session != null) && !session.isValid()) { // 如果Session已经过期,从session管理中删除本对象,就返回空
session = null;
}
if (session != null) {
session.access();
return (session);
}
}
// Create a new session if requested and the response is not committed
if (!create) {
return (null);
}
if (response != null
&& context.getServletContext()
.getEffectiveSessionTrackingModes()
.contains(SessionTrackingMode.COOKIE)
&& response.getResponse().isCommitted()) { // 如果在cookie发送之前已经发送了其他响应内容了,就报错
throw new IllegalStateException(
sm.getString("coyoteRequest.sessionCreateCommitted"));
}
String sessionId = getRequestedSessionId();
if (requestedSessionSSL) {
// If the session ID has been obtained from the SSL handshake then
// use it.
} else if (("/".equals(context.getSessionCookiePath())
&& isRequestedSessionIdFromCookie())) {
if (context.getValidateClientProvidedNewSessionId()) {
boolean found = false;
for (Container container : getHost().findChildren()) {
Manager m = ((Context) container).getManager();
if (m != null) {
try {
if (m.findSession(sessionId) != null) {
found = true;
break;
}
} catch (IOException e) {
// Ignore. Problems with this manager will be
// handled elsewhere.
}
}
}
if (!found) {
sessionId = null;
}
}
} else {
sessionId = null;
}
// manager == org.apache.catalina.session.StandardManager
session = manager.createSession(sessionId);//!!!! 创建Session对象
// Creating a new session cookie based on that session
if (session != null
&& context.getServletContext()
.getEffectiveSessionTrackingModes()
.contains(SessionTrackingMode.COOKIE)) {
// 创建Session的Cookie
Cookie cookie =
ApplicationSessionCookieConfig.createSessionCookie(
context, session.getIdInternal(), isSecure());
// 添加响应Cookie
response.addSessionCookieInternal(cookie);
}
if (session == null) {
return null;
}
session.access();//!!! // 访问次数+1
return session;
}
}
// ----------------------------------
// Session 的调用 -------------的保存(序列化)
// ----------------------------------
class org.apache.catalina.core.StandardContext{
protected synchronized void stopInternal() throws LifecycleException {
// 保存Session信息
// manager == org.apache.catalina.session.StandardManager
Manager manager = getManager();
if (manager instanceof Lifecycle && ((Lifecycle) manager).getState().isAvailable()) {
((Lifecycle) manager).stop();
}
}
}
class org.apache.catalina.session.StandardManager{
protected synchronized void stopInternal() throws LifecycleException {
setState(LifecycleState.STOPPING);
// Write out sessions
try {
unload(); // 保存Session数据到文件中
} catch (Throwable t) {
}
// Expire all active sessions 让活动中的Session全部失效
Session sessions[] = findSessions();
for (int i = 0; i < sessions.length; i++) {
Session session = sessions[i];
if (session.isValid()) {
session.expire(); //
}
}
// Require a new random number generator if we are restarted
super.stopInternal();
}
// 保存Session数据到文件中
public void unload() throws IOException {
doUnload();
}
protected void doUnload() throws IOException {
// Open an output stream to the specified pathname, if any
File file = file();
if (file == null) {
return;
}
// Keep a note of sessions that are expired
ArrayList<StandardSession> list = new ArrayList<>();
try (FileOutputStream fos = new FileOutputStream(file.getAbsolutePath());
BufferedOutputStream bos = new BufferedOutputStream(fos);
ObjectOutputStream oos = new ObjectOutputStream(bos)) { // 写入d:/a/b/tomcat8.x/work/Catalina/localhost/test/SESSIONS.ser
synchronized (sessions) {
// Write the number of active sessions, followed by the details
oos.writeObject(Integer.valueOf(sessions.size()));
Iterator<Session> elements = sessions.values().iterator();
while (elements.hasNext()) {
StandardSession session =
(StandardSession) elements.next();
list.add(session);
session.passivate();
session.writeObjectData(oos); // 写入Session的数据
}
}
}
// Expire all the sessions we just wrote
Iterator<StandardSession> expires = list.iterator();
while (expires.hasNext()) {
// org.apache.catalina.session.StandardSession
StandardSession session = expires.next();
try {
session.expire(false);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
} finally {
session.recycle();
}
}
}
protected File file() {
if (pathname == null || pathname.length() == 0) {
return null;
}
File file = new File(pathname);// SESSIONS.ser
if (!file.isAbsolute()) {
// context === org.apache.catalina.core.StandardContext
Context context = getContext();
ServletContext servletContext = context.getServletContext();
File tempdir = (File) servletContext.getAttribute(ServletContext.TEMPDIR);
if (tempdir != null) {
file = new File(tempdir, pathname); // 从临时目录中加载Session文件
}
}
return file;
}
}