当前位置: 代码迷 >> 综合 >> ObjectMapper 自定义JsonSerializer,报错: has no default (no arg) constructor
  详细解决方案

ObjectMapper 自定义JsonSerializer,报错: has no default (no arg) constructor

热度:22   发布时间:2023-12-06 09:02:32.0

问题及代码:

自定义的Json序列化方式,启用后响应结果可以正常被序列化成功。公共接口在正式响应之前,有一个aspectlog拦截,打印响应结果集,调用ObjectMapper.writeValueAsString(),出现 “has no default (no arg) constructor错误”:

@Slf4j
@Component
@AllArgsConstructor
public class SM4PhoneSerialize extends JsonSerializer {
    private final SM4 sm4;@Overridepublic void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    if (Func.isEmpty(value)) {
    log.info("SM4SensitiveSerialize serialize field is empty");gen.writeString(StringUtils.EMPTY);return;}//序列化需要加密的数据String data = sm4.encryptHex(value.toString());gen.writeString(data);}
}

错误日志:

com.fasterxml.jackson.databind.JsonMappingException: Class cn.com.cnfic.contractmanage.user.dto.service.serialize.SM4PhoneSerialize has no default (no arg) constructor (through reference chain: org.springblade.core.tool.api.R["data"])at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:295)at com.fasterxml.jackson.databind.SerializerProvider.reportMappingProblem(SerializerProvider.java:1309)at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1427)at com.fasterxml.jackson.databind.SerializerProvider.findPrimaryPropertySerializer(SerializerProvider.java:682)at com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap.findAndAddPrimarySerializer(PropertySerializerMap.java:64)at com.fasterxml.jackson.databind.ser.BeanPropertyWriter._findAndAddDynamic(BeanPropertyWriter.java:898)at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:706)at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:755)at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)at com.fasterxml.jackson.databind.ObjectMapper._writeValueAndClose(ObjectMapper.java:4409)at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3663)at org.springblade.core.tool.jackson.JsonUtil.toJson(JsonUtil.java:55)at org.springblade.core.boot.logger.RequestLogAspect.aroundApi(RequestLogAspect.java:162)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:634)at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:624)at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:72)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)at cn.com.cnfic.contractmanage.user.feign.ContractUserResource$$EnhancerBySpringCGLIB$$98136626.getUserInfoByUserId(<generated>)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060)at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962)at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)at javax.servlet.http.HttpServlet.service(HttpServlet.java:497)at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)at javax.servlet.http.HttpServlet.service(HttpServlet.java:584)at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)at org.springblade.core.tool.support.xss.XssFilter.doFilter(XssFilter.java:48)at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)at org.springblade.core.log.filter.LogTraceFilter.doFilter(LogTraceFilter.java:39)at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:124)at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93)at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68)at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:117)at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)at io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52)at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:269)at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:78)at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:133)at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:130)at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:249)at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:78)at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:99)at io.undertow.server.Connectors.executeRootHandler(Connectors.java:387)at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:841)at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2019)at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1558)at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1423)at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalArgumentException: Class cn.com.cnfic.contractmanage.user.dto.service.serialize.SM4PhoneSerialize has no default (no arg) constructorat com.fasterxml.jackson.databind.util.ClassUtil.createInstance(ClassUtil.java:552)at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializerInstance(DefaultSerializerProvider.java:135)at com.fasterxml.jackson.databind.ser.BasicSerializerFactory.findSerializerFromAnnotation(BasicSerializerFactory.java:539)at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._constructWriter(BeanSerializerFactory.java:797)at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanProperties(BeanSerializerFactory.java:604)at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.constructBeanOrAddOnSerializer(BeanSerializerFactory.java:384)at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanOrAddOnSerializer(BeanSerializerFactory.java:286)at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._createSerializer2(BeanSerializerFactory.java:231)at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:165)at com.fasterxml.jackson.databind.SerializerProvider._createUntypedSerializer(SerializerProvider.java:1474)at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1422)... 102 common frames omitted

解决方案:

@Slf4j
@Component
@NoArgsConstructor
public class SM4PhoneSerialize extends JsonSerializer {
    private static SM4 sm4;@Autowiredpublic SM4PhoneSerialize(SM4 sm4) {
    SM4PhoneSerialize.sm4 = sm4;}@Overridepublic void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    if (Func.isEmpty(value)) {
    log.info("SM4SensitiveSerialize serialize field is empty");gen.writeString(StringUtils.EMPTY);return;}//序列化需要加密的数据String data = sm4.encryptHex(value.toString());gen.writeString(data);}
}

解析:

  1. jackson包中,在创建共享的序列化(JsonSerialize)和反序列化(JsonDeserialize)实现的实例时,采用反射的方式,通过ClassUtil工具调用实现类默认的无参构造方法创建。因此,当实现类没有提供默认的无参构造方法时,在调用接口进行序列化或者反序列化的时候,将会抛出该异常。参照代码如下:
public static <T> T createInstance(Class<T> cls, boolean canFixAccess)throws IllegalArgumentException{
    Constructor<T> ctor = findConstructor(cls, canFixAccess);if (ctor == null) {
    throw new IllegalArgumentException("Class "+cls.getName()+" has no default (no arg) constructor");}try {
    return ctor.newInstance();} catch (Exception e) {
    ClassUtil.unwrapAndThrowAsIAE(e, "Failed to instantiate class "+cls.getName()+", problem: "+e.getMessage());return null;}}public static <T> Constructor<T> findConstructor(Class<T> cls, boolean forceAccess)throws IllegalArgumentException{
    try {
    Constructor<T> ctor = cls.getDeclaredConstructor();if (forceAccess) {
    checkAndFixAccess(ctor, forceAccess);} else {
    // Has to be public...if (!Modifier.isPublic(ctor.getModifiers())) {
    throw new IllegalArgumentException("Default constructor for "+cls.getName()+" is not accessible (non-public?): not allowed to try modify access via Reflection: cannot instantiate type");}}return ctor;} catch (NoSuchMethodException e) {
    ;} catch (Exception e) {
    ClassUtil.unwrapAndThrowAsIAE(e, "Failed to find default constructor of class "+cls.getName()+", problem: "+e.getMessage());}return null;}
  1. 自定义序列化和反序列化实现中,需要注入springbean依赖的时候,可以采用类变量来实现,前提是必须将当前实现交于spring容器进行管理。解释:根据类变量的特性,在将当前实现交于spring容器进行管理时,在容器启动创建当前实现的实例时,将会自动注入依赖项;该依赖项为类变量,改动将会作用于该类的所有实例。此种方式实现依赖,会比不需要依赖bean的序列化和反序列化实现多出一个实例。
  相关解决方案