有时候我们不需要用到session,而session在tomcat中是属于关键功能,它在启动的时候会自动创建,这样就会消耗一定的内存空间,如果访问量大了session就会产生很多。这样也不利于我们进行分布扩展。
JSESSIONID的产生机制
tomcat只有在程序中使用了 xxx.getSession() 的时候才会创建session(JSESSIONID是它的标识),其他任何时候都不会主动去创建。
这个时候有人问了,我的程序里明确没有调用 xxx.getSession(),为什么还会创建session?原因是用到了jsp作为渲染机制,你去看一下在tomcat的工作目录中所对应的jsp编译好的.class或者也有.java文件,能够看到:session = pageContext.getSession();这样一段代码。这个session也就是jsp几个内置对象中含有session对象的来源。所以,只要你用到了 jsp,那么就会默认产生session。
那不用jsp,而用 servlet 的话,会不会主动创建 session 呢?不会的,因为对于Controller或者Action来说,本质上还是servlet。不会的,除非你自己写 xxx.getSession()。
那我也不用jsp,用其他的模板引擎,比如 freemarker 或者 Thymeleaf,会不会主动创建 session 呢?不会的,除非这个模板引擎中也主动调用xxx.getSession,否则不会。
那我既不用jsp,也不用servlet,用的是 spring的Controller或者 struts 的Action,那会不会主动创建 session 呢?不会的,因为对于Controller或者Action来说,本质上还是servlet。
解决办法
通过上面的机制分析,很明显禁用tomcat的session的最好途径就是告诉jsp,不要主动创建session,那么方式是在每个jsp页面中加入:
<%@ page session="false" %>
这种方式需要在每一个jsp页面加入以上代码,或者引用共同的jsp。如果某一个页面忘记引用或者是JAVA部分代码主动的去获取了xxx.getSession(),那么还是会产生session。
所以本人倾向于第二种方式,在tomcat中停止创建session,自定义一个SessionManager
package com.noSession;
import java.io.IOException;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Session;
import org.apache.catalina.session.ManagerBase;
public class SessionManager extends ManagerBase implements Lifecycle {
@Override
protected synchronized void startInternal() throws LifecycleException {
super.startInternal();
setState(LifecycleState.STARTING);
}
@Override
protected synchronized void stopInternal() throws LifecycleException {
setState(LifecycleState.STOPPING);
}
@Override
public void load() throws ClassNotFoundException, IOException {
}
@Override
public void unload() throws IOException {
}
@Override
public Session createSession(String sessionId) {
return null;
}
@Override
public Session createEmptySession() {
return null;
}
}
注意,上面这个SessionManager中createSession、createEmptySession方法返回的是null。
在使用jsp的时候会报错Page needs a session and none is available。
有一下两种解决方案:
1.在jsp中加上
<%@ page session="false" %>
2.全局使用同一个session
import java.io.IOException;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Session;
import org.apache.catalina.session.ManagerBase;
public class SessionManager extends ManagerBase implements Lifecycle {
private Session globalSession;
@Override
protected synchronized void startInternal() throws LifecycleException {
super.startInternal();
setState(LifecycleState.STARTING);
}
@Override
protected synchronized void stopInternal() throws LifecycleException {
setState(LifecycleState.STOPPING);
}
@Override
public void load() throws ClassNotFoundException, IOException {
}
@Override
public void unload() throws IOException {
}
@Override
public Session createSession(String sessionId) {
if(globalSession == null) {
globalSession = super.createSession(sessionId);
} else {
globalSession.setValid(true);
}
return globalSession;
}
@Override
public Session createEmptySession() {
if(globalSession == null) {
globalSession = super.createSession(sessionId);
} else {
globalSession.setValid(true);
}
return globalSession;
}
}
还需要注意这个globalSession需要进行激活,否则在tomcat的session失效后,会将此session标注为失效,那么尽管globalSession还是存在的,但是是失效的状态,还是会报错说session不存在。所以globalSession.setValid(true);这句很重要。
然后,在tomcat的server.xml中进行设置,或者配置host的其他方式
<Context docBase="D:\test" path="/test" reloadable="false" sessionCookieName="yoursessionname">
<Manager className="xxx.xxx.SessionManager" />
</Context>
上面的sessionCookieName默认是JSESSIONID,其实这个是有点点缺陷的,能够让别人知道你的应用是用java开发的,所以,换个其他的名字会更好一些。
最后,将SessionManager打包为jar包,放到tomcat的lib文件夹下面