使用Fastjson解析內(nèi)部類的一個小問題

使用的Fastjson的版本是1.2.7和1.2.47兩個版本。

1. 問題

使用Fastjson解析包含內(nèi)部類的對象時,發(fā)生異常,代碼大致如下:

public class HelloController {
   
    public User test1(HttpServletRequest request) {
        UserInfo userInfo = new UserInfo();
        userInfo.setAge(12);
        userInfo.setName("test");
        String json = JSON.toJSONString(userInfo);
        UserInfo userInfo1 = JSON.parseObject(json, UserInfo.class);
        System.out.println(userInfo1);
        return new User();
    }

    class UserInfo {
        private String name;
        private Integer age;
        // 省略get,set方法
    }
}

出現(xiàn)問題的方法是 parseObject 方法:

public static <T> T parseObject(String text, Class<T> clazz)

出現(xiàn)的異常:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is com.alibaba.fastjson.JSONException: can't create non-static inner class instance.

2. 原因

??由于我們創(chuàng)建的 UserInfo 屬于非靜態(tài)內(nèi)部類,而非靜態(tài)內(nèi)部類的實例化是依賴于外部類的實例的,必須先實例化外部類,然后再通過外部類來實例化,也就是說我們無法直接實例化內(nèi)部類,而Fastjson則也是直接實例化對象,由于實例化不成功,所以會拋出異常。而針對靜態(tài)內(nèi)部類來說,實例化靜態(tài)內(nèi)部類是不依賴于外部類的實例,直接實例化靜態(tài)內(nèi)部類即可,所以解決方式也就很顯而易見了。

// 非靜態(tài)內(nèi)部類無法直接實例化,拋出異常
HelloController.UserInfo userInfo2 = new HelloController.UserInfo();

// 需要先實例化外部類,再通過外部類的實例來實例化
HelloController helloController = new HelloController();
HelloController.UserInfo userInfo = helloController.new UserInfo();

我們可以來看部分源碼,根據(jù)異常鏈,我們可以定位到 JavaBeanDeserializer 的createInstance方法:

Constructor<?> constructor = beanInfo.defaultConstructor;
if (beanInfo.defaultConstructorParameterSize == 0) {
    if (constructor != null) {
        object = constructor.newInstance();
    } else {
        object = beanInfo.factoryMethod.invoke(null);
    }
} else {
    ParseContext context = parser.getContext();
    if (context == null || context.object == null) {
        throw new JSONException("can't create non-static inner class instance.");
    }
    ...
}

這里,首先會先判斷JavaBeanInfo對象的defaultConstructorParameterSize屬性是否是0,如果等于0,會執(zhí)行初始化操作,而該屬性的值的設置在:

defaultConstructorParameterSize = defaultConstructor.getParameterTypes().length;

所以defaultConstructorParameterSize 屬性就表示構(gòu)造方法參數(shù)的個數(shù),而這個值是通過Constructor對象的getParameterTypes來獲取的;而在靜態(tài)內(nèi)部類中,構(gòu)造方法的參數(shù)個數(shù)是0,非靜態(tài)內(nèi)部類中,構(gòu)造方法的參數(shù)個數(shù)是1;因為非靜態(tài)內(nèi)部類會通過構(gòu)造方法來設置外部類的引用,而靜態(tài)內(nèi)部類則不會。這點可以通過javap來命令來查看字節(jié)碼,針對內(nèi)部類的部分字節(jié)碼如下:

public controller.HelloController();
Code:
   0: aload_0
   1: invokespecial #1                  // Method java/lang/Object."<init>":()V
   4: return

public entity.User test1(javax.servlet.http.HttpServletRequest);
Code:
   0: new           #2                  // class controller/HelloController$UserInfo
   3: dup
   4: aload_0
   5: invokespecial #3                  // Method controller/HelloController$UserInfo."<init>":(Lcontroller/HelloController;)V

可以看到最后的invokespecial指令,將外部類的引用作為構(gòu)造方法的參數(shù)。

3. 解決方式

??由于非靜態(tài)內(nèi)部類不能直接實例化,所以我們直接將內(nèi)部類修改為靜態(tài)內(nèi)部類即可,當然也可以把內(nèi)部類拆成一個外部類也可以。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容