通过fastjson反序列化某javabean时失败,并抛出以下异常:
Could not deserialize: autoType is not support. com.xxx.Shop; nested exception is com.alibaba.fastjson.JSONException: autoType is not support. com.xxx.Shop
at com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer.deserialize(GenericFastJsonRedisSerializer.java:37)
然而只有项目第一次反序列化该javabean时会抛异常失败,复现方式只有重启项目后的第一次反序列化操作。
过程
查询官方文档,开启AutoType的方法
enable_autotype。
但是文档中的方法均测试无效,无奈debug源码,发现抛出异常的代码为
com.alibaba.fastjson.parser.ParserConfig的1125行代码:
JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, clazz, propertyNamingStrategy);
if (beanInfo.creatorConstructor != null && autoTypeSupport) {
throw new JSONException("autoType is not support. " + typeName);
}
经与正常反序列化的javabean比较后,发现反序列化失败的javabean在如上代码执行时,beanInfo.creatorConstructor的值不为null,最后抛出autoType is not support的异常。
继续追踪,在com.alibaba.fastjson.util.JavaBeanInfo283行代码判断反序列化失败的javabean为接口或抽象类:
boolean isInterfaceOrAbstract = clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers());
再于com.alibaba.fastjson.util.JavaBeanInfo381行代码的if判断分支下给beanInfo.creatorConstructor赋值:
else if (!isInterfaceOrAbstract){
...
for (Constructor constructor : constructors) {
Class<?>[] parameterTypes = constructor.getParameterTypes();
if (className.equals("org.springframework.security.web.authentication.WebAuthenticationDetails")) {
if (parameterTypes.length == 2 && parameterTypes[0] == String.class && parameterTypes[1] == String.class) {
creatorConstructor = constructor;
creatorConstructor.setAccessible(true);
paramNames = ASMUtils.lookupParameterNames(constructor);
break;
}
}
if (className.equals("org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken")) {
if (parameterTypes.length == 3
&& parameterTypes[0] == Object.class
&& parameterTypes[1] == Object.class
&& parameterTypes[2] == Collection.class) {
creatorConstructor = constructor;
creatorConstructor.setAccessible(true);
paramNames = new String[] {"principal", "credentials", "authorities"};
break;
}
}
if (className.equals("org.springframework.security.core.authority.SimpleGrantedAuthority")) {
if (parameterTypes.length == 1
&& parameterTypes[0] == String.class) {
creatorConstructor = constructor;
paramNames = new String[] {"authority"};
break;
}
}
boolean is_public = (constructor.getModifiers() & Modifier.PUBLIC) != 0;
if (!is_public) {
continue;
}
String[] lookupParameterNames = ASMUtils.lookupParameterNames(constructor);
if (lookupParameterNames == null || lookupParameterNames.length == 0) {
continue;
}
if (creatorConstructor != null
&& paramNames != null && lookupParameterNames.length <= paramNames.length) {
continue;
}
paramNames = lookupParameterNames;
// 有参数构造方法给beanInfo.creatorConstructor赋值
creatorConstructor = constructor;
}
}
最终定位项目中通过fastjson反序列化失败,是因为该javabean没有无参构造方法,被fastjson判断为接口或抽象类,最终抛出autoType is not support的异常。
解决
给javabean添加无参构造方法。
我的代码中的bean添加了无参构造方法,同时也做了如下设置,但是仍报错,后来排查是运维在jvm参数添加的-Dfastjson=safemode导致的,改成TrimStringFieldValue就可以了,我使用的fastjson是1.2.74版本,同时设置成安全模式时1.2.70版本不会有这个错误。
static
{
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
}