在SSH2框架下,当和某些前台JS的Ajax框架,例如:EasyUI、Ext等结合使用的时候,常用到struts2中result的类型type为json的result,这个时候可以很方便的将Action中的域通过Struts2的json插件转化为JSON字符串传递给前天Ajax框架使用。
但是,当配合Hibernate使用,要向前台通过JSON方式传递Hibernate实体类中级联的其他实体类时,系统就会报NoSuchMethod:getHibernateLazyInitializer()异常。这里主要的原因是Hibernate会将实体类内部级联的其他实体类类型的域做类似于反射机制的处理,用以实现Hibernate内部的Lazyload或其他神奇的特性,这样做会使Stuts2对实体类级联JSON序列化的时候,报出NoSuchMethod:getHibernateLazyInitializer()异常。
解决方式1:
给实体类添加getHibernateLazyInitializer()方法,这个方法虽然可以避免报错,但是应用程序启动的时候,系统会报MultiMethed异常。虽然在使用Hibernate的时候并未发现这种方式的修改会给系统运行带来什么困扰,但是由于开发中时间短,没有时间完全的评估出MutiMethod异常究竟对Hibernate的使用产生何种影响,所以不推荐使用这种方法,会有使用隐患。
解决方式2:
从struts2的json插件动手,struts2中type为json的result可以有多个参数设置:
<result type="json">
<!-- 要JSON序列化的action中的属性名,其实就是对getXXX方法的返回值进行序列化 -->
<param name="root">list</param>
<!-- JSON序列化时不需要序列化的属性的正则表达式,这里的"\"是转义字符,[d+]表示了List中的index,hibernateLazyInitializer就是是否调用getHibernateLazyInitializer方法,当然也可以添加
类似的其他正则表达式,这里就不一一列举了 -->
<param name="excludeProperties">\[\d+\]\.hibernateLazyInitializer,\[\d+\]\.rack\.hibernateLazyInitializer,\[\d+\]\.connectbox\.hibernateLazyInitializer</param>
<!-- JSON序列化时是否去除值为null的属性 -->
<param name="excludeNullProperties">true</param>
<!-- JSON序列化时需要序列化的属性的正则表达式,这里的"\"是转义字符,[d+]表示了List中的index,hibernateLazyInitializer就是是否调用getHibernateLazyInitializer方法,当然也可以添加
类似的其他正则表达式,这里就不一一列举了,注意:excludeProperties与includeProperties一般不同时使用,当然也有特例,请实际情况使用 -->
<param name="includeProperties">
\[\d+\]\.id,\[\d+\]\.rack\.name,\[\d+\]\.connectbox\.id,\[\d+\]\.connectbox\.name,\[\d+\]\.columns,\[\d+\]\.block,\[\d+\]\.linePairNumber
</param>
</result>
这里在使用的时候,感觉既然已经写出了不需要序列化的属性、方法,为什么系统还会报NoSuchMethod:getHibernateLazyInitializer()呢?查看struts2的json插件的源代码(JSONWriter类)发现,其原来的代码是,先序列化各个属性,然后再对所有序列化出的属性,根据参数设定哪些要过滤。
所以我修改了其源代码,先根据条件过滤,然后再序列化,只是调换了其代码的顺序。修改后的JSONWrite类,修改了bean(Object object)方法,将原来的221-232行,提前到193-204行。修改后的JSONWriter类在文章最后。
上边两种解决方式,推荐使用第二种,有写的不对的地方,请指正、一起讨论,谢谢!