Environment:
Spring boot- 1.2.3
Issue :
When BindingResult
is added as next argument to @Valid
argument for controller method like below, keep on getting java.lang.StackOverflowError
@RequestMapping(value = "/employees", method = RequestMethod.POST, consumes = "application/json")
public void createEmployee(HttpServletRequest request, @Valid @RequestBody Employee employee, BindingResult result){
logger.debug("Creating Employee [" + employee.getForename() + " " + employee.getSurname() + "]");
}
If BindingResult
method argument is removed, it works fine.
Update start
Found the issue, StackOverflowError
occurred while converting BindingResult
instance to JSON
using below code:
//log all method arguments
com.google.gson.Gson gson = new com.google.gson.Gson();
String json = gson.toJson(bindingResultArgFromControllerMethod);
Framework code uses Gson
to convert method arguments to JSON
for logging.
Is there way to avoid/handle this exception?
Update end
Relevant Stack trace :
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler processing failed; nested exception is java.lang.StackOverflowError] with root cause
java.lang.StackOverflowError: null
//Repeatattive block start
at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:383)
at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:378)
//Repeatattive block end
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:155)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:97)
at com.google.gson.Gson.getAdapter(Gson.java:407)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getFieldAdapter(ReflectiveTypeAdapterFactory.java:136)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.access$100(ReflectiveTypeAdapterFactory.java:49)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.<init>(ReflectiveTypeAdapterFactory.java:106)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:105)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:161)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:97)
at com.google.gson.Gson.getAdapter(Gson.java:407)
According to the API docs of BindingResult, this is only a holder of the data that the user entered to the browser. Moreover, the implementation of BindingResult can contain all sort of references to other helper objects. Based on the stack trace, there's a circular reference on the current implementation of BindingResult
.
I think you want to jsonify the data the user entered. Then all you need to do is to jsonify the target
of the BindingResult
:
com.google.gson.Gson gson = new com.google.gson.Gson();
String json = gson.toJson(bindingResultArgFromControllerMethod.getTarget());
This target
is, according to the method docs: the wrapped target object, which may be a bean, an object with public fields, a Map - depending on the concrete binding strategy.
Hope this helps.