当Jsonplugin遇到CGLIB
JsonPlugin在分析类结构并序列化时,对于CGLig动态生成的类也是按照一般类来看待的。这就导致了如下的问题:
在一个应用中,某些情况下,一个服务类返回的实体并不是原有实体类的对象,而是CGLib动态生成的子类。例如使用Hibernate的时候,某些情况下DAO返回的是EntityClassName$$EnhancerByCGLIB$$ac21e这样的类的对象。Hibernate在这个子类中添加了hibernateLazyInitializer等等的附加属性。由于jsonplugin并不区分类和动态生成的类,所以也会试图序列化hibernateLazyInitializer属性,从而导致出现如下的异常:
java.sql.SQLException: Positioned Update not supported.
at com.mysql.jdbc.ResultSet.getCursorName(ResultSet.java:1800)
另外,CGLIB生成的类,某些方法上的@JSON标记奇怪的丢失了。导致标记了@JSON(serialize=false)的属性也被序列化。
在网上查了很久没有发现相关的文章,所以无奈就自己动手修改jsonplugin的代码了。
类:com.googlecode.jsonplugin.JSONWriter,修改bean()方法:
private void bean(Object object) throws JSONException {this.add("{");BeanInfo info;try {Class clazz = object.getClass();info = ((object == this.root) && this.ignoreHierarchy) ? Introspector.getBeanInfo(clazz, clazz.getSuperclass()): Introspector.getBeanInfo(clazz);PropertyDescriptor[] props = info.getPropertyDescriptors();boolean hasData = false;for (int i = 0; i < props.length; ++i) {PropertyDescriptor prop = props[i];String name = prop.getName();Method accessor = prop.getReadMethod();Method baseAccessor = null; //这里增加一个临时变量作为真实希望序列化的属性的accessor方法引用if (clazz.getName().indexOf("$$EnhancerByCGLIB$$") > -1) { //如果是CGLIB动态生成的类try {//下面的逻辑是根据CGLIB动态生成的类名,得到原本的实体类名//例如 EntityClassName$$EnhancerByCGLIB$$ac21e这样//的类,将返回的是EntityClassName这个类中的相应方法,若//获取不到对应方法,则说明要序列化的属性例如hibernateLazyInitializer之类//不在原有实体类中,而是仅存在于CGLib生成的子类中,此时baseAccessor//保持为nullbaseAccessor = Class.forName(clazz.getName().substring(0,clazz.getName().indexOf("$$"))).getDeclaredMethod(accessor.getName(),accessor.getParameterTypes());} catch (Exception ex) {log.debug(ex.getMessage());}}else//若不是CGLib生成的类,那么要序列化的属性的accessor方法就是该类中的方法。baseAccessor = accessor;//这个判断,根据上面的逻辑,使得仅存在于CGLIB生成子类中的属性跳过JSON序列化if (baseAccessor != null) {//下面的JSON Annotation的获取也修改为从baseAccessor获取,这样避免了//由于CGLIB生成子类而导致某些方法上的JSON Annotation丢失导致处理不该//序列化的属性JSON json = baseAccessor.getAnnotation(JSON.class);if (json != null) {if (!json.serialize())continue;else if (json.name().length() > 0)name = json.name();}//ignore "class" and othersif (this.shouldExcludeProperty(clazz, prop)) {continue;}String expr = null;if (this.buildExpr) {expr = this.expandExpr(name);if (this.shouldExcludeProperty(expr)) {continue;}expr = this.setExprStack(expr);}if (hasData) {this.add(',');}hasData = true;Object value = accessor.invoke(object, new Object[0]);this.add(name, value, accessor);if (this.buildExpr) {this.setExprStack(expr);}}}} catch (Exception e) {throw new JSONException(e);}this.add("}");}