0

I'm newer in java development. I want to add validators to our project. But I had a problem. Please help me!

I add

<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>

in my pom.xml .My project is a Maven Modules Project.

My MVC Config:

<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> 

And my 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;
    }


}

controller

@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));
}

then I test, but log show:

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>

I have no idea what's wrong. I google,but I can't find something else like this, pls help me. thank you!


According to m4gic's suggestion.I try code a demo:

controller

@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;
    }
}

But It works well.NO error;Result:

{
    "code": 0,
    "msg": "name is null!",
    "data": {
        "name": null,
        "age": 0
    }
}
  • In the provided code I have not found any suspicious. Could you show as your User4BlackListResponse bean too? – m4gic Aug 29 '18 at 08:55
  • Are you using the Java Persistence API? If so your issue could be somewhere else in your code and related to this: https://stackoverflow.com/q/3325387/3465375 – Sir Celsius Aug 29 '18 at 08:59
  • @m4gic sure,I have added. – kissshot13 Aug 29 '18 at 09:49
  • @SirCelsius I use TKmybatis – kissshot13 Aug 29 '18 at 09:49
  • After I checked again the error, I think your problem is related to the validation framework. Please try out what happen when you remove the @Valid from the request attribute. Another thing that would worth a try to remove the DataResult.SuccessData wrapper from the returned object and return the list only. Although I think the first will be the cause. – m4gic Aug 29 '18 at 10:04
  • @m4gic I try to remove the @ Valid but it doesn't work.I remove Errors ,The error disappears,but the Validation doesn't work.So I think it must be something wrong with it. – kissshot13 Aug 29 '18 at 12:26
  • Maybe some version information would be useful too, could you please give us the JDK version, spring version, jackson version? It would be good to know the full qualified name of DataResult, if it is written by you, then (if it is possible) share it please that one too. – m4gic Aug 29 '18 at 14:12
  • @m4gic jdk verizon is 1.7.0_09 ,Spring version is 4.3.2.RELEASE,jackson is 2.8.7.shiro version is 1.2.6. When I change return Type DataResult to String ,errers still exist.when I debug,the error occurred before entering the controller. I hava added all config about the project. – kissshot13 Aug 30 '18 at 02:40
  • Please have a look at [this](https://stackoverflow.com/questions/9337824/how-to-return-error-status-and-validation-errors-from-this-spring-mvc-controller) it seems you are in similar shoes. – m4gic Aug 30 '18 at 06:26

2 Answers2

0

Since you are using Hibernate, check if you have bidirectional mapping. Those can cause infinite recursion. For example if you have parent class A and child class B, and A has list of B`s but B also have reference to its parent A. This will be major problem for serialization. Solution is to either break bidirectional mapping or implement some kind of guard to not populate field of type A in class B.

Mladen Savić
  • 472
  • 4
  • 10
0

I think what you definitely can do is to try to handle validation errors in different way.

Maybe the problem is that you are trying to handle errors in a controller function having @ResponseBody that would mean that the error should be given back as json. If you would use controlleradvice or custom exception handler, that may help you out.

The other thing you can do is to create a proper spring boot project (after figuring out that using boot 1.4.0.RELEASE will create a project that will contain spring 4.3.2.RELEASE and everything else that is compatible). Then try to reproduce your error in this project (with boot-controlled compatible dependency-set). If there is no error, then you have to compare the two projects dependency tree, because very probably you are using some incompatible jars.

m4gic
  • 1,461
  • 12
  • 19
  • Thank you for your patience.I wrote a demo of springmvc.I'll show you above.It works very well with @ResponseBody. – kissshot13 Aug 30 '18 at 10:13