Tomcat中Session对象部分属性值缺失问题的分析及解决方法从中提取时,在SESSION会话刚创建的时候,这个对象的部分属性在一段时间内是正确的,但是一段时间后,虽然SESSION没有失效,但是这部分属性的值变成了NULL。更让人吃惊的是,无论是变成了NULL的属性还是没有变成NULL的属性,都是最简单的String类型变量,让人无法理解它们之间的区别。为了把问题表述清楚,我举个例子详细说明一下。我们定义的类信息大致如下图所示:其中,SessionBean是我们要放入SESSION中的对象,BaseBean是SessionBean继承的父类。无论在开发还是测试环境下,SessionBean对象都可以访问值,不会抛出任何异常,但是里面的属性不一定:SESSION创建后,只要SESSION不失效,属性a和属性b总是可以正常读取。取其值;但是属性c只在SESSION创建后不久的一段时间内有效。如果一段时间后取值,则属性c的值将变为NULL。没有任何操作,变量值发生了变化,是不是很神奇?[RootRoot]中间具体的排错过程就不说了。总之,经过长时间的分析排查,终于找到了问题的根源——BaseBean没有继承Serializable接口!【原因分析】我结合上面和我对Tomcat服务器SESSION会话管理原理的分析,给大家解释一下为什么只继承了一个较少可序列化的接口(Serializable),就会出现这么奇怪的问题。首先,在创建SESSION时,SessionBean对象中的所有属性(a,b,c)都可以正常写入SESSION会话中,是没有问题的。对于新创建的SESSION,此时它的内容保存在服务器的内存中。如果此时读取SEESION,Tomcat会直接将内存中的内容返回给调用者,所以返回的属性值都是正确的。那么无论是因为Tomcat占用内存超过阈值,还是Tomcat定时保存SESSION站点,都会导致接下来的操作无论如何——持久化SESSION会话。在将数据从内存写入硬盘的过程中,涉及到一个很重要的问题:数据应该以什么格式存储在硬盘中,又应该如何转换成相应的格式?这是——连载!通过观察上图的代码,不难发现SessionBean对象实现了Serializable接口,所以在将SessionBean对象保存到磁盘的过程中,也就是对象被序列化的时候,是不会报错的有什么错误,这也是导致我们很难通过跟踪错误日志来定位问题的根本原因。但是由于SessionBean的基类BaseBean没有实现Serializable接口,导致序列化时无法保存BaseBean中的属性c,反序列化时属性c的值变为NULL。所以我们读到的值也莫名其妙的变成了NULL也就不难理解了。【解决方案】至于解决方案,很简单,在基类BaseBean中增加一个Serializable接口即可。【问题反思】这个问题给我们带来的反思主要有两个:***,对于保存在SESSION会话中的类,我们必须保证该类和该类的父类都实现了Serializable接口,否则该类不能进行属性序列化。其次,在进行业务逻辑操作之前,需要对所有不确定值的合法性进行安全检查(防御性编程)。被判定为正确的属性c(因为写的是完全可控可信的)已经验证了合法性。这个神奇的问题什么时候被发现还不确定。所以,防御性编程非常重要。
