Tomcat JDBC Store持久化管理器访问静态资源时抛空指针异常
问题分析与解决方案
问题根源
你遇到的空指针异常是Tomcat PersistentValve的设计缺陷导致的:它默认假设同一会话在同一时间只会有一个请求处理,但加载静态资源时,浏览器会并发发起多个请求(比如CSS、JS、图片),这会触发会话在JDBCStore中的并发写操作,最终抛出异常。
临时修复:让静态资源绕过PersistentValve
如果不想立刻替换JDBCStore,可以通过配置让静态资源请求不经过PersistentValve,避免并发冲突:
- 在Tomcat的
context.xml中修改PersistentValve配置,添加exclude属性排除静态资源路径:
<Valve className="org.apache.catalina.valves.PersistentValve" exclude="/security/images/*,/security/css/*,/security/js/*"/>
- 若使用Spring Boot自定义Tomcat配置,可在代码中添加Valve并设置排除规则:
@Bean public TomcatServletWebServerFactory tomcatFactory() { return new TomcatServletWebServerFactory() { @Override protected void postProcessContext(Context context) { PersistentValve valve = new PersistentValve(); valve.setExclude("/security/images/*,/security/css/*,/security/js/*"); context.getPipeline().addValve(valve); } }; }
替代的会话外部化管理方案(无需JDBCStore/PersistentValve)
1. Redis分布式会话(推荐)
这是Spring Boot多节点会话共享的主流方案,性能高、生态完善,天然支持并发请求:
- 集成步骤:
- 添加Maven依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> - 在
application.yml中配置Redis连接:spring: redis: host: your-redis-host port: 6379 password: your-redis-password session: store-type: redis timeout: 3600s # 会话超时时间 - 在启动类添加注解启用Redis会话:
@SpringBootApplication @EnableRedisHttpSession public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
- 添加Maven依赖:
2. Hazelcast分布式会话
适合不想依赖Redis的场景,Hazelcast支持嵌入式集群(节点自动发现)或独立集群部署:
- 集成步骤:
- 添加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-hazelcast</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-hazelcast</artifactId> </dependency> - 嵌入式模式无需额外集群配置,独立集群需在配置文件中指定节点地址
- 启动类添加注解:
@SpringBootApplication @EnableHazelcastHttpSession public class YourApplication { // ... }
- 添加依赖:
3. Tomcat原生会话复制
适合小规模集群(节点数≤5),无需外部存储,通过网络复制会话到其他节点:
- 配置要点:
- 在Tomcat的
server.xml中配置集群:<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8"> <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <Membership className="org.apache.catalina.tribes.membership.McastService" address="228.0.0.4" port="45564" frequency="500" dropTime="3000"/> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="auto" port="4000" autoBind="100" selectorTimeout="5000" maxThreads="6"/> <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/> </Sender> <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/> </Channel> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/> <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster> - 在项目的
web.xml中添加<distributable/>标签启用分布式会话
- 在Tomcat的
- 注意:所有会话对象必须实现
Serializable接口,集群规模过大时会产生明显网络开销。
4. Memcached分布式会话
适合已有Memcached集群的场景,通过Spring Session集成:
- 添加
spring-session-memcached及spymem客户端依赖,配置Memcached地址后,在启动类添加@EnableMemcachedHttpSession注解即可。
总结建议
- 优先选择Redis分布式会话:性能优异、生态成熟,适配绝大多数多节点Spring Boot场景,完全解决并发会话问题。
- 小规模集群可考虑Hazelcast或Tomcat会话复制:无需额外存储服务,但要注意会话序列化和集群规模限制。
内容的提问来源于stack exchange,提问作者sagarshah1983
相关产品推荐
相关产品推荐

