com.fasterxml.jackson.databind.JsonMappingException:无限递归

时间:2018-08-29 08:47:57

标签: java spring hibernate-validator

我是Java开发的新手。我想在我们的项目中添加验证器。但是我有一个问题。请帮帮我!

我添加

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.1.0.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.2.0.Final</version>
</dependency>

在我的pom.xml中。我的项目是Maven模块项目。

我的MVC配置:

<bean
    id="validator"
    class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property
        name="providerClass"
        value="org.hibernate.validator.HibernateValidator" />
    <property
        name="validationMessageSource"
        ref="messageSource" />
</bean>
<bean
    id="messageSource"
    class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <value>classpath:development/messages</value>
        </list>
    </property>
    <property
        name="useCodeAsDefaultMessage"
        value="false" />
    <property
        name="defaultEncoding"
        value="UTF-8" />
    <property
        name="cacheSeconds"
        value="60" />
</bean> 

还有我的DTO:

public class User4BlackListRequest implements Serializable {

    private static final long serialVersionUID = -4886429042153823406L;

    @Size(max=4,min=2,message="realName's length must be 2 ~ 4")
    private String realName;

    private Integer isDelete;

    private UserTypeEnum userType;

    public User4BlackListRequest(){}

    public String getRealName() {
        return realName;
    }

    public void setRealName(String realName) {
        this.realName = realName;
    }
    public Integer getIsDelete() {
        return isDelete;
    }
    public void setIsDelete(Integer isDelete) {
        this.isDelete = isDelete;
    }
    public void setUserType(UserTypeEnum userType) {
        this.userType = userType;
    }
    public UserTypeEnum getUserType() {
        return userType;
    }
}

User4BlackListResponse

public class User4BlackListResponse implements Serializable {


    private static final long serialVersionUID = -4175005832458864444L;

    private String userId;

    private String realName;


    private UserTypeEnum userType;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getRealName() {
        return realName;
    }

    public void setRealName(String realName) {
        this.realName = realName;
    }

    public UserTypeEnum getUserType() {
        return userType;
    }

    public void setUserType(UserTypeEnum userType) {
        this.userType = userType;
    }


}

控制器

@RequestMapping(value="/getList4BlackList",method=RequestMethod.GET)
@ResponseBody
public DataResult<PageInfo<User4BlackListResponse>> getList4BlackList (@Valid User4BlackListRequest request,Errors result, PageParam pageParam,SortParam sortParam){
     if(result.hasErrors()){
         String msg = result.getAllErrors().get(0).getDefaultMessage();
         logger.info(msg);
     }
     return DataResult.SuccessData(userService.getList4BlackList(request, pageParam, sortParam));
}

然后我测试,但日志显示:

com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: java.util.LinkedHashMap["org.springframework.validation.BindingResult.user4BlackListRequest"]->org.springframework.validation.BeanPropertyBindingResult["model"]->java.util.LinkedHashMap["org.springframework.validation.BindingResult.user4BlackListRequest"]->org.springframework.validation.BeanPropertyBindingResult["model"]->java.util.LinkedHashMap["org.springframework.validation.BindingResult.user4BlackListRequest"]->org.springframework.validation.BeanPropertyBindingResult["model"]->java.util.LinkedHashMap["org.springframework.validation.BindingResult.user4BlackListRequest"]-....at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:706) at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:633)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:536)
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:30)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:704)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:690)

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:task="http://www.springframework.org/schema/task"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd     
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/task
    http://www.springframework.org/schema/task/spring-task-3.1.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd" >

    <mvc:annotation-driven validator="validator" />

    <task:scheduled-tasks scheduler="myScheduler">  
        <task:scheduled ref="userScheduledManager" method="editActive" cron="0 0/15 * * * *"/>
        <task:scheduled ref="invitScheduledManager" method="expireInvite" cron="0 0/60 * * * *"/>
    </task:scheduled-tasks>  
    <task:scheduler id="myScheduler" pool-size="10"/>

    <bean id="apiObjectMapper"
        class="com.xxxxx.zpxt.common.convert.ApiObjectMapper">
        <constructor-arg name="jsonDefaultValue" value="true"></constructor-arg>
        <property name="timeZone">
            <bean class="java.util.TimeZone" factory-method="getTimeZone">
                <constructor-arg value="GMT+08" />
            </bean>
        </property>
        <property name="dateFormat">
            <bean class="java.text.SimpleDateFormat">
                <constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" />
            </bean>
        </property>
    </bean>

    <context:component-scan base-package="com.xxxxx.zpxt">
        <context:exclude-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <bean id="taskExecutor"
        class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"
        scope="singleton" lazy-init="true">
        <property name="corePoolSize" value="4" />   
        <property name="maxPoolSize" value="80" />   
        <property name="queueCapacity" value="500" />  
    </bean>

    <bean id="conversionService"
        class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <list>
                <bean
                    class="com.xxxxx.zpxt.common.convert.ValueToEnumConverterFactory" />
                <bean
                    class="com.xxxxx.zpxt.common.convert.StringToDateConverter"></bean>
            </list>
        </property>
    </bean>

    <bean id="loggerMethodInterceptor" class="com.xxxxx.zpxt.common.interceptor.LoggerMethodInterceptor"/>  
    <bean id="repeatSubmitMethodInterceptor" class="com.xxxxx.zpxt.common.interceptor.RepeatSubmitMethodInterceptor"/>  
    <bean id="kickoutMethodInterceptor" class="com.xxxxx.zpxt.common.interceptor.KickoutMethodInterceptor"/>  
    <bean id="cacheServiceInterceptor" class="com.xxxxx.zpxt.common.interceptor.CacheServiceInterceptor"/>

    <bean
        id="validator"
        class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
        <property
            name="providerClass"
            value="org.hibernate.validator.HibernateValidator" />
        <property
            name="validationMessageSource"
            ref="messageSource" />
    </bean>
    <bean
        id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basenames">
            <list>
            </list>
        </property>
        <property
            name="useCodeAsDefaultMessage"
            value="false" />
        <property
            name="defaultEncoding"
            value="UTF-8" />
        <property
            name="cacheSeconds"
            value="60" />
    </bean>  
</beans>  

servlet-context.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.0.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
       http://www.springframework.org/schema/aop       
       http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <context:component-scan base-package="com.xxxxx.zpxt.controller"
        use-default-filters="false">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>
    <context:component-scan
        base-package="com.xxxxx.zpxt.shiro.web.controller"
        use-default-filters="false">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>


    <mvc:annotation-driven conversion-service="conversionService">
        <mvc:argument-resolvers>
            <bean
                class="com.xxxxx.zpxt.controller.util.CurrentUserMethodArgumentResolver" />
        </mvc:argument-resolvers>
    </mvc:annotation-driven>


    <aop:config>
        <aop:pointcut id="webMethodPointcut"
            expression="execution(* com.xxxxx.zpxt.controller.*.*.*(..)) and  
        @annotation(org.springframework.web.bind.annotation.RequestMapping)" />
        <aop:advisor advice-ref="loggerMethodInterceptor" pointcut-ref="webMethodPointcut" order="1"/>
        <aop:advisor advice-ref="kickoutMethodInterceptor" pointcut-ref="webMethodPointcut" order="2"/>
        <aop:advisor advice-ref="repeatSubmitMethodInterceptor" pointcut-ref="webMethodPointcut" order="3"/>
    </aop:config>


    <mvc:default-servlet-handler />


    <mvc:resources mapping="/static/**" location="/WEB-INF/static/" />


    <bean id="defaultViewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver"
        p:order="1">
        <property name="contentType" value="text/html" />
        <property name="prefix" value="/" />
        <property name="suffix" value="" />
    </bean>

    <bean id="handlerExceptionResolver"
        class="com.xxxxx.zpxt.common.exception.BusinessHandlerExceptionResolver">
        <constructor-arg name="objectMapper" ref="apiObjectMapper"></constructor-arg>
    </bean>


    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="utf-8" />
        <property name="maxUploadSize" value="20971520" />
        <property name="maxInMemorySize" value="4096" />
    </bean>


    <import resource="classpath*:/mvc-module/*-module.xml" />
</beans>

我不知道怎么了。我用google搜索,但是找不到其他类似的东西,请帮帮我。谢谢!


根据m4gic的建议。我尝试编写演示代码:

控制器

@RequestMapping(value="/index")
@ResponseBody
public DataResult<User> index(@Valid User request,Errors errors){
        User user =  new User();
        DataResult<User> result = new DataResult<>();
        if(errors.hasErrors()){
            result.setMsg(errors.getAllErrors().get(0).getDefaultMessage());
        }
        result.setData(user);
         return result;
     }

User.java

public class User implements Serializable { 
       private static final long serialVersionUID = 1L;
        @NotBlank(message = "name is null!")
        private String name;
        private int age;
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
}

DataResult.java

public class DataResult<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    private int code;
    private String msg;
    private T data;
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
}

但是效果很好。没有错误;结果:

{
    "code": 0,
    "msg": "name is null!",
    "data": {
        "name": null,
        "age": 0
    }
}

2 个答案:

答案 0 :(得分:0)

由于使用的是Hibernate,请检查是否具有双向映射。那些可能导致无限递归。例如,如果您有父类A和子类B,并且A有B的列表,但B也有其父A的引用。这将是序列化的主要问题。解决方案是要么中断双向映射,要么实施某种防护措施以不填充类B中的类型A的字段。

答案 1 :(得分:0)

我认为您绝对可以做的是尝试以不同的方式处理验证错误。

也许问题在于您正在尝试处理具有@ResponseBody的控制器函数中的错误,这意味着该错误应作为json返回。如果您使用controlleradvicecustom exception handler,可能会有所帮助。

您可以做的另一件事是创建一个正确的spring boot项目(在弄清楚使用引导1.4.0.RELEASE将创建一个包含spring 4.3.2.RELEASE以及其他所有内容的项目之后)。兼容)。然后尝试重现此项目中的错误(具有引导控制的兼容依赖项集)。如果没有错误,则必须比较两个项目的依赖关系树,因为很可能您正在使用一些不兼容的jar。