You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tomcat JDBC Store持久化管理器访问静态资源时抛空指针异常

问题分析与解决方案

问题根源

你遇到的空指针异常是Tomcat PersistentValve的设计缺陷导致的:它默认假设同一会话在同一时间只会有一个请求处理,但加载静态资源时,浏览器会并发发起多个请求(比如CSS、JS、图片),这会触发会话在JDBCStore中的并发写操作,最终抛出异常。


临时修复:让静态资源绕过PersistentValve

如果不想立刻替换JDBCStore,可以通过配置让静态资源请求不经过PersistentValve,避免并发冲突:

  1. 在Tomcat的context.xml中修改PersistentValve配置,添加exclude属性排除静态资源路径:
<Valve className="org.apache.catalina.valves.PersistentValve" 
       exclude="/security/images/*,/security/css/*,/security/js/*"/>
  1. 若使用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多节点会话共享的主流方案,性能高、生态完善,天然支持并发请求:

  • 集成步骤:
    1. 添加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>
      
    2. 在application.yml中配置Redis连接:
      spring:
        redis:
          host: your-redis-host
          port: 6379
          password: your-redis-password
        session:
          store-type: redis
          timeout: 3600s # 会话超时时间
      
    3. 在启动类添加注解启用Redis会话:
      @SpringBootApplication
      @EnableRedisHttpSession
      public class YourApplication {
          public static void main(String[] args) {
              SpringApplication.run(YourApplication.class, args);
          }
      }
      

2. Hazelcast分布式会话

适合不想依赖Redis的场景,Hazelcast支持嵌入式集群(节点自动发现)或独立集群部署:

  • 集成步骤:
    1. 添加依赖:
      <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>
      
    2. 嵌入式模式无需额外集群配置,独立集群需在配置文件中指定节点地址
    3. 启动类添加注解:
      @SpringBootApplication
      @EnableHazelcastHttpSession
      public class YourApplication {
          // ...
      }
      

3. Tomcat原生会话复制

适合小规模集群(节点数≤5),无需外部存储,通过网络复制会话到其他节点:

  • 配置要点:
    1. 在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>
      
    2. 在项目的web.xml中添加<distributable/>标签启用分布式会话
  • 注意:所有会话对象必须实现Serializable接口,集群规模过大时会产生明显网络开销。

4. Memcached分布式会话

适合已有Memcached集群的场景,通过Spring Session集成:

  • 添加spring-session-memcached及spymem客户端依赖,配置Memcached地址后,在启动类添加@EnableMemcachedHttpSession注解即可。

总结建议

  • 优先选择Redis分布式会话:性能优异、生态成熟,适配绝大多数多节点Spring Boot场景,完全解决并发会话问题。
  • 小规模集群可考虑Hazelcast或Tomcat会话复制:无需额外存储服务,但要注意会话序列化和集群规模限制。

内容的提问来源于stack exchange,提问作者sagarshah1983

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 03:50:24