I have a class that annotated with #RestController and #ControllerAdvice that has my requests mapped methods (#RequestMapping). Also in this class I added a method public void initBinder(WebDataBinder dataBinder) that is annotated with #InitBinder and responsible to register some custom editor.
Specifically, it is a propertyEditor that convert String to Enum.
I noticed that on every call to #RequestMapping method in my controller, my initBinder method is being called. Since in my opinion this editor registration should happen only once (initialization of controller), I want this to be set (called) only once.
Is there a way to do so?
Alternatively, you can use implementation of BindingInitializer for registering your custom Editor. You have to define a bean of class "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" in your app context file and set its "webBindingInitializer" property with your implementation of BindingInitializer.
In any case, scope of WebDataBinder is for a request. Hope this helps.
Related
Where can I find some examples on replacing/migrating Spring Framework controllers from version 2 to version 4? By that I mean migrate/replace SimpleFormController and BaseCommandController into an annotated controller #Controller. I am new to the Spring Framework.
For example, my old controller uses onBind(HttpServletRequest request, Object command) method. How can I migrate method like onBind and onBindAndvalidate using the new spring libraries?
Thank you
I was recently doing this upgradation task and I found a very helpful guide to do upgradation.
posting here so that it can be used further by other potential developers.
SimpleFormController vs #Controller
In XML-based Spring MVC web application, you create a form controller by extending the SimpleFormController class.
In annotation-based, you can use #Controller instead
formBackingObject() vs RequestMethod.GET
In SimpleFormController, you can initialize the command object for binding in the formBackingObject() method. In annotation-based, you can do the same by annotated the method name with #RequestMapping(method = RequestMethod.GET).
onSubmit() vs RequestMethod.POST
In SimpleFormController, the form submission is handle by the onSubmit() method. In annotation-based, you can do the same by annotated the method name with #RequestMapping(method = RequestMethod.POST).
referenceData() vs #ModelAttribute
In SimpleFormController, usually you put the reference data in model via referenceData() method, so that the form view can access it. In annotation-based, you can do the same by annotated the method name with #ModelAttribute.
initBinder() vs #InitBinder
In SimpleFormController, you define the binding or register the custom property editor via initBinder() method. In annotation-based, you can do the same by annotated the method name with #InitBinder.
From Validation
In SimpleFormController, you have to register and map the validator class to the controller class via XML bean configuration file, and the validation checking and work flows will be executed automatically.
In annotation-based, you have to explicitly execute the validator and define the validation flow in the #Controller class manually.
You can see the example implementation screenshots at Click Here
In the following controller
#Controller
public class MyFormController {
#InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}
// ...
}
The class does not inherit any other class or it doesn't have any WebDataBinder instance variable. How come then the custom editors are stored and where?
The Spring MVC infrastructure is complex. There are a lot of pieces that come together to invoke your #Controller handler method. I explain some of it in my answer here.
In summary, Spring MVC scans your #Controller classes for #RequestMapping annotated methods. It creates mappings for these in a RequestMappingHandlerMapping and uses a RequestMappingHandlerAdapter to dispatch to them, ie. invoke your handler methods.
Before it invokes the method, it goes through a couple of housekeeping steps. You can see these in the source code, here. The short version is:
It wraps the HttpServletRequest and HttpServletResponse in an adapter.
It creates factories producing Model and WebDataBinder instances (the latter come from your #InitBinder methods).
It creates a ServletInvocableHandlerMethod which encapsulates the invocation of your handler method with HandlerMethodArgumentResolver to generate arguments for your handler method and HandlerMethodReturnValueHandler to process the return value of your handler method.
It creates a ModelAndViewContainer which will potentially be used to later render a view as the HTTP response.
It prepares an async environment in case the request uses async components.
Then it invokes the method.
That WebDataBinder object you're curious about is stored and used in various places in the execution context described above.
I am looking into Dispatcher Servlet code. Here i found that dispatcher servlet uses HandlerMapping to select the handler for the request. Also, RequestMappingHandlerMapping is used as an implementation for HandlerMapping. Now, isHandlerMethod of RequestMappingHandlerMapping returns true if the bean under consideration has either #Controller or #RequestMapping annotation. If certain bean has only #RequestMapping annotation applied at class level would it still be considered as Handler?.
Any Help would be greatly appreciated.
The #RequestMapping and #Controller annotation have different meanings. The request mapping is used to decide, which class / method is used to handle a request to a speciffic URL. If you look at the source of the #Controller adnotation you will find that it is annotated with #Component itself. This way it can be used to set up the component scan that will register an instance of the class as a bean.
I'm quessing that, since those annotations are usually used together, it's done so that a minute performance gain can be achieved. Also, you could declare your controllers differently, either by java config or xml.
Edit:
I have done a quick prototype with a controller bean declared in java config, without the #Controller annotation. The answer is yes, the method will be used to handle a request, even if the class is not annotated.
I created a validator to validate date field of any class. I autowired its instance in my controller and called it's validate() in my controller's method. Everything is working fine.
#RequestMapping(value = "/{pan}",method=RequestMethod.POST)
public ModelAndView submitIfPANpresents(#ModelAttribute("command") #Valid PortfolioBean portfolio,bindingResult result){
dateValidator.validate(portfolio, result);
if(result.hasErrors()){
System.out.println(result.getErrorCount());
return new ModelAndView("portfolioview");
}
:
}
Now I created a method in my controller to bind custom validator with WebDataBinder. I did this with hope that I would have not to call validate() explicitly.
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(dateValidator);
}
Still everything is working fine.
Now I commented call for validate() of custom validator from controller's method and tested the code. I can see errors set from custom validator only. Spring's internal validation stops working.
What wrong I am doing or missing to do? My need is, not to call validate() explicitly and to bind validator from xml as an extra validator.
When you do binder.setValidator(dateValidator);, you replace Spring's validator with your dateValidator. That's why Spring's internal validation stops working.
So if you want both validators to work, you should remove this line and keep dateValidator.validate(...). You have no choice if you want to have an extra validator.
I am trying to migrate from Spring 2.0 to Spring 3.0.
Previously I defined a controller MyController inheriting from SimpleFormController and have some logic written in the onSubmit method. All my controllers having the handler methods are inherited from MyController. Thus, the logic written in onSubmit of MyController used to get executed for all requests.
Now as I migrate to annotated controller wherein my controller is a simple pojo, how do I ensure the execution of onSubmit everytime? One way is to call onSubmit from all handler methods of all the controllers. This is cumbersome.
Can anyone suggest any feasible solution. As annotating formBackingObject with #ModelAttribute ensures the invocation for all requests, isn't there an analogy for onSubmit method?
If you want to perform the same action before each invokation of any annotated controller, you could use an interceptor. You can write your own interceptor by just implementing the preHandle method.
You will then need to register this interceptor in the DefaultAnnotationHandlerMapping or whatever Handler mapping you use to dispatch to your controllers.
Registering interceptors is explained in this article:
http://www.scottmurphy.info/spring_framework_annotation_based_controller_interceptors
Annotate the method you wish to invoke. The method signature is very flexible. Take a look at the docs for #RequestMapping
#RequestMapping(value={"/foo"}, method=RequestMethod.POST)
public String myMethod(many options for parameters) {...
Ok so if i understand correctly you want inheritance to continue to play a role in the stack when a request is handled by a controller. You can extend any class in an #RequestMapping annotated POJO but you will have to define an #override method to annotate it. All you do basically is call super with the arguments in the overriding method. If you extend an annotated class and both are declared as Controller then you will get an exception since the route will be defined more then once.
it would look like this
public class Pojo{
public String someBaseMethod(){
return "";
}
}
#Controller
public class ChildController extends Pojo {
#Override
#RequestMapping("/do_it")
public String someBaseMethod() {
return super.someBaseMethod();
}
}
A good case could be made to use composition over inheritance. I even suggest that you use the filtering mechanism instead if it can apply to perform common operations. AOP could also be a good tool.