JsonPlugin在分析類結構并序列化時,對于CGLig動態(tài)生成的類也是按照一般類來看待的。這就導致了如下的問題:
在一個應用中,某些情況下,一個服務類返回的實體并不是原有實體類的對象,而是CGLib動態(tài)生成的子類。例如使用Hibernate的時候,某些情況下DAO返回的是EntityClassName$$EnhancerByCGLIB$$ac21e這樣的類的對象。Hibernate在這個子類中添加了hibernateLazyInitializer等等的附加屬性。由于jsonplugin并不區(qū)分類和動態(tài)生成的類,所以也會試圖序列化hibernateLazyInitializer屬性,從而導致出現如下的異常:
at com.mysql.jdbc.ResultSet.getCursorName(ResultSet.java:1800)
另外,CGLIB生成的類,某些方法上的@JSON標記奇怪的丟失了。導致標記了@JSON(serialize=false)的屬性也被序列化。
在網上查了很久沒有發(fā)現相關的文章,所以無奈就自己動手修改jsonplugin的代碼了。
類:com.googlecode.jsonplugin.JSONWriter,修改bean()方法:
1 private void bean(Object object) throws JSONException {
2 this.add("{");
3
4 BeanInfo info;
5
6 try {
7 Class clazz = object.getClass();
8
9 info = ((object == this.root) && this.ignoreHierarchy) ? Introspector
10 .getBeanInfo(clazz, clazz.getSuperclass())
11 : Introspector.getBeanInfo(clazz);
12
13 PropertyDescriptor[] props = info.getPropertyDescriptors();
14
15 boolean hasData = false;
16 for (int i = 0; i < props.length; ++i) {
17 PropertyDescriptor prop = props[i];
18 String name = prop.getName();
19 Method accessor = prop.getReadMethod();
20 Method baseAccessor = null; //這里增加一個臨時變量作為真實希望序列化的屬性的accessor方法引用
21 if (clazz.getName().indexOf("$$EnhancerByCGLIB$$") > -1) { //如果是CGLIB動態(tài)生成的類
22 try {
23 //下面的邏輯是根據CGLIB動態(tài)生成的類名,得到原本的實體類名
24 //例如 EntityClassName$$EnhancerByCGLIB$$ac21e這樣
25 //的類,將返回的是EntityClassName這個類中的相應方法,若
26 //獲取不到對應方法,則說明要序列化的屬性例如hibernateLazyInitializer之類
27 //不在原有實體類中,而是僅存在于CGLib生成的子類中,此時baseAccessor
28 //保持為null
29 baseAccessor = Class.forName(
30 clazz.getName().substring(0,
31 clazz.getName().indexOf("$$")))
32 .getDeclaredMethod(accessor.getName(),
33 accessor.getParameterTypes());
34 } catch (Exception ex) {
35 log.debug(ex.getMessage());
36 }
37 }
38 else //若不是CGLib生成的類,那么要序列化的屬性的accessor方法就是該類中的方法。
39 baseAccessor = accessor;
40
41 //這個判斷,根據上面的邏輯,使得僅存在于CGLIB生成子類中的屬性跳過JSON序列化
42 if (baseAccessor != null) {
43
44 //下面的JSON Annotation的獲取也修改為從baseAccessor獲取,這樣避免了
45 //由于CGLIB生成子類而導致某些方法上的JSON Annotation丟失導致處理不該
46 //序列化的屬性
47 JSON json = baseAccessor.getAnnotation(JSON.class);
48 if (json != null) {
49 if (!json.serialize())
50 continue;
51 else if (json.name().length() > 0)
52 name = json.name();
53 }
54
55 //ignore "class" and others
56 if (this.shouldExcludeProperty(clazz, prop)) {
57 continue;
58 }
59 String expr = null;
60 if (this.buildExpr) {
61 expr = this.expandExpr(name);
62 if (this.shouldExcludeProperty(expr)) {
63 continue;
64 }
65 expr = this.setExprStack(expr);
66 }
67 if (hasData) {
68 this.add(',');
69 }
70 hasData = true;
71
72 Object value = accessor.invoke(object, new Object[0]);
73 this.add(name, value, accessor);
74 if (this.buildExpr) {
75 this.setExprStack(expr);
76 }
77 }
78 }
79 } catch (Exception e) {
80 throw new JSONException(e);
81 }
82
83 this.add("}");
84 }
2 this.add("{");
3
4 BeanInfo info;
5
6 try {
7 Class clazz = object.getClass();
8
9 info = ((object == this.root) && this.ignoreHierarchy) ? Introspector
10 .getBeanInfo(clazz, clazz.getSuperclass())
11 : Introspector.getBeanInfo(clazz);
12
13 PropertyDescriptor[] props = info.getPropertyDescriptors();
14
15 boolean hasData = false;
16 for (int i = 0; i < props.length; ++i) {
17 PropertyDescriptor prop = props[i];
18 String name = prop.getName();
19 Method accessor = prop.getReadMethod();
20 Method baseAccessor = null; //這里增加一個臨時變量作為真實希望序列化的屬性的accessor方法引用
21 if (clazz.getName().indexOf("$$EnhancerByCGLIB$$") > -1) { //如果是CGLIB動態(tài)生成的類
22 try {
23 //下面的邏輯是根據CGLIB動態(tài)生成的類名,得到原本的實體類名
24 //例如 EntityClassName$$EnhancerByCGLIB$$ac21e這樣
25 //的類,將返回的是EntityClassName這個類中的相應方法,若
26 //獲取不到對應方法,則說明要序列化的屬性例如hibernateLazyInitializer之類
27 //不在原有實體類中,而是僅存在于CGLib生成的子類中,此時baseAccessor
28 //保持為null
29 baseAccessor = Class.forName(
30 clazz.getName().substring(0,
31 clazz.getName().indexOf("$$")))
32 .getDeclaredMethod(accessor.getName(),
33 accessor.getParameterTypes());
34 } catch (Exception ex) {
35 log.debug(ex.getMessage());
36 }
37 }
38 else //若不是CGLib生成的類,那么要序列化的屬性的accessor方法就是該類中的方法。
39 baseAccessor = accessor;
40
41 //這個判斷,根據上面的邏輯,使得僅存在于CGLIB生成子類中的屬性跳過JSON序列化
42 if (baseAccessor != null) {
43
44 //下面的JSON Annotation的獲取也修改為從baseAccessor獲取,這樣避免了
45 //由于CGLIB生成子類而導致某些方法上的JSON Annotation丟失導致處理不該
46 //序列化的屬性
47 JSON json = baseAccessor.getAnnotation(JSON.class);
48 if (json != null) {
49 if (!json.serialize())
50 continue;
51 else if (json.name().length() > 0)
52 name = json.name();
53 }
54
55 //ignore "class" and others
56 if (this.shouldExcludeProperty(clazz, prop)) {
57 continue;
58 }
59 String expr = null;
60 if (this.buildExpr) {
61 expr = this.expandExpr(name);
62 if (this.shouldExcludeProperty(expr)) {
63 continue;
64 }
65 expr = this.setExprStack(expr);
66 }
67 if (hasData) {
68 this.add(',');
69 }
70 hasData = true;
71
72 Object value = accessor.invoke(object, new Object[0]);
73 this.add(name, value, accessor);
74 if (this.buildExpr) {
75 this.setExprStack(expr);
76 }
77 }
78 }
79 } catch (Exception e) {
80 throw new JSONException(e);
81 }
82
83 this.add("}");
84 }
這樣修改之后,原有類中不能存在的屬性將不會被序列化,同時,由于不檢查生成的類的方法上的JSON標記,而是檢查原有類上的標記,這樣避免了由于CGLIB導致的Annotation丟失的問題。
在此依然向諸位詢問是否JSONPlugin有處理這樣的情況的方法。