CacheInterceptor缓存序列化问题

问题描述:

在JFinal中使用了CacheInterceptor,对一些常用的数据进行缓存。CacheInterceptor使用Ehcache作为底层的缓存实现,Ehcache支持持久化,但是在配置了Ehcache持久化之后,会出现无法序列化的问题。

报错信息:

20:59:46.724 [http-nio-8080-exec-71] ERROR net.sf.ehcache.store.disk.DiskStorageFactory call - Disk Write of /api/v3/works/allActivity failed: 
java.io.NotSerializableException: org.apache.tomcat.util.net.jsse.JSSESupport
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) ~[?:1.8.0_144]
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) ~[?:1.8.0_144]
	at java.util.HashMap.internalWriteEntries(HashMap.java:1785) ~[?:1.8.0_144]
	at java.util.HashMap.writeObject(HashMap.java:1362) ~[?:1.8.0_144]
	at sun.reflect.GeneratedMethodAccessor1867.invoke(Unknown Source) ~[?:?]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_144]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_144]
	at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1028) ~[?:1.8.0_144]
	at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496) ~[?:1.8.0_144]
	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) ~[?:1.8.0_144]
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) ~[?:1.8.0_144]
	at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548) ~[?:1.8.0_144]
	at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:441) ~[?:1.8.0_144]
	at net.sf.ehcache.Element.writeObject(Element.java:875) ~[ehcache-2.10.5.jar:2.10.5]
	at sun.reflect.GeneratedMethodAccessor1871.invoke(Unknown Source) ~[?:?]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_144]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_144]
	at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1028) ~[?:1.8.0_144]
	at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496) ~[?:1.8.0_144]
	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) ~[?:1.8.0_144]
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) ~[?:1.8.0_144]
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) ~[?:1.8.0_144]
	at net.sf.ehcache.util.MemoryEfficientByteArrayOutputStream.serialize(MemoryEfficientByteArrayOutputStream.java:97) ~[ehcache-2.10.5.jar:2.10.5]
	at net.sf.ehcache.store.disk.DiskStorageFactory.serializeElement(DiskStorageFactory.java:403) ~[ehcache-2.10.5.jar:2.10.5]
	at net.sf.ehcache.store.disk.DiskStorageFactory.write(DiskStorageFactory.java:385) ~[ehcache-2.10.5.jar:2.10.5]
	at net.sf.ehcache.store.disk.DiskStorageFactory$DiskWriteTask.call(DiskStorageFactory.java:477) [ehcache-2.10.5.jar:2.10.5]
	at net.sf.ehcache.store.disk.DiskStorageFactory$PersistentDiskWriteTask.call(DiskStorageFactory.java:1071) [ehcache-2.10.5.jar:2.10.5]
	at net.sf.ehcache.store.disk.DiskStorageFactory$IndexWriteTask.call(DiskStorageFactory.java:1108) [ehcache-2.10.5.jar:2.10.5]
	at net.sf.ehcache.store.disk.DiskStorageFactory.unbind(DiskStorageFactory.java:921) [ehcache-2.10.5.jar:2.10.5]
	at net.sf.ehcache.store.disk.DiskStore.dispose(DiskStore.java:664) [ehcache-2.10.5.jar:2.10.5]
	at net.sf.ehcache.store.CacheStore.dispose(CacheStore.java:342) [ehcache-2.10.5.jar:2.10.5]
	at net.sf.ehcache.Cache.dispose(Cache.java:2588) [ehcache-2.10.5.jar:2.10.5]
	at net.sf.ehcache.CacheManager.shutdown(CacheManager.java:1548) [ehcache-2.10.5.jar:2.10.5]
	at com.jfinal.plugin.ehcache.EhCachePlugin.stop(EhCachePlugin.java:95) [jfinal-java8-3.4.jar:?]
	at com.jfinal.core.JFinal.stopPlugins(JFinal.java:115) [jfinal-java8-3.4.jar:?]
	at com.jfinal.core.JFinalFilter.destroy(JFinalFilter.java:91) [jfinal-java8-3.4.jar:?]
	at org.apache.catalina.core.ApplicationFilterConfig.release(ApplicationFilterConfig.java:314) [catalina.jar:9.0.1]
	at org.apache.catalina.core.StandardContext.filterStop(StandardContext.java:4523) [catalina.jar:9.0.1]
	at org.apache.catalina.core.StandardContext.stopInternal(StandardContext.java:5319) [catalina.jar:9.0.1]
	at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:257) [catalina.jar:9.0.1]
	at org.apache.catalina.core.ContainerBase.removeChild(ContainerBase.java:831) [catalina.jar:9.0.1]
	at org.apache.catalina.startup.HostConfig.undeploy(HostConfig.java:1427) [catalina.jar:9.0.1]
	at org.apache.catalina.startup.HostConfig.checkResources(HostConfig.java:1335) [catalina.jar:9.0.1]
	at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1642) [catalina.jar:9.0.1]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_144]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_144]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_144]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_144]
	at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:287) [tomcat-coyote.jar:9.0.1]
	at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819) [?:1.8.0_144]
	at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801) [?:1.8.0_144]
	at org.apache.catalina.manager.ManagerServlet.check(ManagerServlet.java:1481) [catalina.jar:9.0.1]
	at org.apache.catalina.manager.ManagerServlet.deploy(ManagerServlet.java:729) [catalina.jar:9.0.1]
	at org.apache.catalina.manager.ManagerServlet.doPut(ManagerServlet.java:424) [catalina.jar:9.0.1]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:663) [servlet-api.jar:?]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) [servlet-api.jar:?]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [catalina.jar:9.0.1]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.1]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) [tomcat-websocket.jar:9.0.1]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:9.0.1]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.1]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) [catalina.jar:9.0.1]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [catalina.jar:9.0.1]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:592) [catalina.jar:9.0.1]
	at org.apache.catalina.valves.RequestFilterValve.process(RequestFilterValve.java:348) [catalina.jar:9.0.1]
	at org.apache.catalina.valves.RemoteAddrValve.invoke(RemoteAddrValve.java:52) [catalina.jar:9.0.1]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [catalina.jar:9.0.1]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [catalina.jar:9.0.1]
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:651) [catalina.jar:9.0.1]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [catalina.jar:9.0.1]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [catalina.jar:9.0.1]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:500) [tomcat-coyote.jar:9.0.1]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-coyote.jar:9.0.1]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:754) [tomcat-coyote.jar:9.0.1]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1376) [tomcat-coyote.jar:9.0.1]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-coyote.jar:9.0.1]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_144]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_144]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:9.0.1]
	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_144]

问题原因:

以上的报错信息就是说在进行序列化的时候, org.apache.tomcat.util.net.jsse.JSSESupport没有实现java.io.Serializable接口,导致序列化失败。

经过查看源码得知,在CacheInterceptor缓存数据时,会将request中的attribute也放到缓存中。

protected void cacheAction(String cacheName, String cacheKey, Controller controller) {
   HttpServletRequest request = controller.getRequest();
   Map<String, Object> cacheData = new HashMap<String, Object>();
   for (Enumeration<String> names=request.getAttributeNames(); names.hasMoreElements();) {
      String name = names.nextElement();
      //将request中的attribute放入缓存中
      cacheData.put(name, request.getAttribute(name));
   }
   
   Render render = controller.getRender();
   if (render != null) {
      cacheData.put(renderKey, createRenderInfo(render));       // cache RenderInfo
   }
   CacheKit.put(cacheName, cacheKey, cacheData);
}


当开启ssl后,request.getAttributeNames()会额外取到5个参数

javax.servlet.request.ssl_session

javax.servlet.request.ssl_session_id

javax.servlet.request.ssl_session_mgr

javax.servlet.request.key_size

javax.servlet.request.cipher_suite

其中javax.servlet.request.ssl_session_mgr是org.apache.tomcat.util.net.jsse.JSSESupport类型的,无法序列化


解决方法:

写一个MyCacheInterceptor,重写cacheAction方法,如下:

@Override
protected void cacheAction(String cacheName, String cacheKey, Controller controller) {
    HttpServletRequest request = controller.getRequest();
    Map<String, Object> cacheData = new HashMap<>();
    for (Enumeration<String> names = request.getAttributeNames(); names.hasMoreElements(); ) {
        String name = names.nextElement();
        Object attribute = request.getAttribute(name);
        //只有可以序列化的属性才放入缓存中
        if (attribute instanceof Serializable) {
            cacheData.put(name, attribute);
        }
    }

    Render render = controller.getRender();
    if (render != null) {
        cacheData.put(renderKey, createRenderInfo(render));        // cache RenderInfo
    }
    CacheKit.put(cacheName, cacheKey, cacheData);
}

其他:

  1. CacheInterceptor中有一个useCacheDataAndRender,将缓存中的request属性写入最新的request中,不知道该操作有什么用处,如果不把javax.servlet.request.ssl_session_mgr对应的属性写入最新的reqeust中是否存在问题?波总如果看到了麻烦解答下,谢谢~~

  2. 参考:https://gitee.com/jfinal/jfinal/issues/10


    上面的网址是“Tomcat 下开启ssl之后 renderJson()报错”的问题,也是由于reqeust中的不可序列化属性导致的,已经修复。


评论区

hb963724769

2019-10-25 16:44

@JFinal 此处呼叫大佬,我也遇到这个问题了

JFinal

2019-10-25 17:09

@hb963724769 按照提示来,如果某个类的对象需要放缓存,让其实现一下 Serializable 接口即可,这个是 java 基础,与 jfinal 是无关的

一条狗

2020-04-16 12:42

我也碰到了这个问题,楼主你这样解决了,那这个缓存请求参数不匹配,缓存不就没用了么

一条狗

2020-04-16 12:45

我感觉这个CacheInterceptor 应该避开ssl那几个参数做一下特殊处理@JFinal

JFinal

2020-04-16 20:55

@一条狗 开发者向 cache 中存放什么数据,这个是 jfinal 无法预判的,哪怕今天预判是 ssl 有关变量并解决了, 下次肯定还会出来更多其它类型的变量

pfjia

2020-09-24 21:51

@一条狗 上述处理方式并不会导致请求参数不匹配,因为请求参数都是 Serializable ,不会被过滤

热门分享

扫码入社