Custom bean instantiation logic in Spring MVC - spring

I have a Spring MVC application trying to use a rich domain model, with the following mapping in the Controller class:
#RequestMapping(value = "/entity", method = RequestMethod.POST)
public String create(#Valid Entity entity, BindingResult result, ModelMap modelMap) {
if (entity== null) throw new IllegalArgumentException("An entity is required");
if (result.hasErrors()) {
modelMap.addAttribute("entity", entity);
return "entity/create";
}
entity.persist();
return "redirect:/entity/" + entity.getId();
}
Before this method gets executed, Spring uses BeanUtils to instantiate a new Entity and populate its fields. It uses this:
...
ReflectionUtils.makeAccessible(ctor);
return ctor.newInstance(args);
Here's the problem:
My entities are Spring managed beans. The reason for this is to inject DAOs on them. Instead of calling new, I use EntityFactory.createEntity(). When they're retrieved from the database, I have an interceptor that overrides the
public Object instantiate(String entityName, EntityMode entityMode, Serializable id)
method and hooks the factories into that.
So the last piece of the puzzle missing here is how to force Spring to use the factory rather than its own BeanUtils reflective approach? Any suggestions for a clean solution?
Thanks very much in advance.

You can use #ModelAttribute-annotated method to pre-populate the model with your bean. Then data binder will use that bean instead of instantiating the new one. However, this will affect all method of the controller.
#ModelAttribute
public Entity createEntity() { ... }

Related

Spring AOP & HttpServletRequest

I am working on an annotation that will be sending some audit events to the other microservice.
Say, I am creating an entity and I have a method add on my Rest controller.
#PostMapping
#Audit
public ResponseEntity<EntityDTO> add(EntityDTO entity){
...
}
I have an appropriate Aspect defined, that is associated with the #Audit annotation.
But here is a trick, the nature of an audit event dictates that I need to extract some metadata from the HttpServletRequest itself.
And I do not want to modify my signature by adding (or replacing my only argument) HttpServletRequest object.
How can I pass HttpServletRequest into my aspect? Is there some elegant way?
Since you're using spring MVC, consider Spring MVC interceptors instead of "generic" aspects.
These are natively supported by Spring MVC and can provide access to both the handler and HttpServletRequest object
See this tutorial for using the interceptors and general configuration
See This thread for some information about the handler
final HandlerMethod handlerMethod = (HandlerMethod) handler; // this is what you'll get in the methods of the interceptor in the form of Object
final Method method = handlerMethod.getMethod();
Following is how it can be done with Spring AOP .
Example annotation.
#Retention(RUNTIME)
#Target({ TYPE, METHOD })
public #interface Audit {
String detail();
}
And the corresponding aspect
#Component
#Aspect
public class AuditAspect {
#Around("#annotation(audit) && within(com.package.web.controller..*)")
public Object audit(ProceedingJoinPoint pjp, Audit audit) throws Throwable {
// Get the annotation detail
String detail = audit.detail();
Object obj = null;
//Get the HttpServletRequest currently bound to the thread.
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
try {
// audit code
System.out.println(detail);
System.out.println(request.getContextPath());
obj = pjp.proceed();
} catch (Exception e) {
// Log Exception;
}
// audit code
return obj;
}
}
NB : Op has accepted interceptor based answer. This answer is to demonstrate the Spring AOP code to achieve the requirement.
Hope this helps

Spring Mvc ModelAttribute with referencing name is not working?

I want to to create different #Entity entities within the same Controller.
#RequestMapping(value="create", method=RequestMethod.GET)
public String GET(Model model) throws InstantiationException, IllegalAccessException
{
Class<?> clazz = ????; // a Random POJO is chosen, i want to use POJOs!!
Object object = clazz.newInstance();
model.addAttribute("object", object);
return "create";
}
#RequestMapping(value="create", method=RequestMethod.POST)
public #ResponseBody Object POST(#ModelAttribute(value="object") Object object)
{
System.out.println("POST! got type: " + object.getClass().getName());
return object;
}
In the Post Method I get NULL for #ModelAttribute(value="object") Object object
If I change it to #ModelAttribute(value="object") realType object it is working perfectly fine. But I don't know the type yet.
I thought the #ModelAttribute can achieve this anyway with the name "object" but apparently not. What am I missing?
There is no actual model object named object when you submit, spring constructs it based on the parameter type and will bind the properties accordingly.
You have 2 choices to make it work
Store the object in the session
Use a #ModelAttribute annotated method
If neither of these are there spring will simply look at the method argument and use reflection to construct an instance of that class. So in your case it will only be Object and after that binding will fail.
Store object in the session
#Controller
#SessionAttributes("object")
public class MyController { ... }
Make sure that when you are finished that you call the setComplete() method on a SessionStatus object.
Use a #ModelAttribute annotated method
Instead of creating and adding the object in a request handling method create a speficic method for it.
#ModelAttribute("object")
public Object formBackingObject() {
Class<?> clazz = ????; // a Random POJO is chosen, i want to use POJOs!!
Object object = clazz.newInstance();
return object;
}
This method will be called before each request handling method, so that a newly fresh object is constructed which will be used for binding.

Spring Controller fetch query parameter for a wrapper request class

I am trying to build RESTful web service by using spring 4.0
I have a controller:
#Controller
#RequestMapping("test")
public class Controller{
#RequestMapping("fetch",method=RequestMethod.GET)
#ResponseStatus(value=HttpStatus.OK)
#ResponseBody
public ResultResponse fetchController(ResultRequest req){
if((req.getName).equals("John"))
return new ResultResponse(100);
else
return new ResultResponse(0);
}
}
and my ResultRequest.class
public class ResultRequest{
private String name;
//getter,setter
}
If I hit the url to //host//contextPath/test/fetch?name=John
the controller will return the object ResultResponse(100)
my question is, there no #RequestParam or other annotation in the request parameter,
how does the spring controller know to set the query parameter "name" as the property of wrapper class
ResultRequest ?
Thanks
Spring uses implementations of an interface called HandlerMethodArgumentResolver for resolving arguments to pass to handler methods, ie. methods annotated with #RequestMapping.
One of these is a ModelAttributeMethodProcessor. Its javadoc states
Resolves method arguments annotated with #ModelAttribute and handles
return values from methods annotated with #ModelAttribute.
Model attributes are obtained from the model or if not found possibly
created with a default constructor if it is available. Once created,
the attributed is populated with request data via data binding and
also validation may be applied if the argument is annotated with
#javax.validation.Valid.
When this handler is created with annotationNotRequired=true, any
non-simple type argument and return value is regarded as a model
attribute with or without the presence of an #ModelAttribute.
Spring registers two objects of this type. One to handle parameters annotated with #ModelAttribute and one to handle ones without.
Further reading:
Form submit in Spring MVC 3 - explanation
An Errors/BindingResult argument is expected to be declared immediately after the model attribute, the #RequestBody or the #RequestPart arguments

RedirectAttributes giving IllegalStateException in Spring 3.1

I'm want to use RedirectAttibutes property that has come up in Spring 3.1, I have the following handler method for post in my controller
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String register(#ModelAttribute("admin") Admin admin, BindingResult bindingResult, SessionStatus sessionStatus, RedirectAttributes redirectAttributes) {
redirectAttributes.addAttribute("admin", admin);
if (bindingResult.hasErrors()) {
return REGISTRATION_VIEW;
}
sessionStatus.setComplete();
return "redirect:list";
}
But when I submit the form I'm getting the following exception:
java.lang.IllegalStateException: Argument [RedirectAttributes] is of type Model or Map but is not assignable from the actual model. You may need to switch newer MVC infrastructure classes to use this argument.
org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:322)
I have come across a few gotcha's with redirectAttributes that you cannot use ModelAndView as the return type. So I returned just the string view.
Can anyone pl. tell me where I'm going wrong?
Thanks.
Spring 3.1 introduced new version of Spring MVC backend implementation (RequestMappingHandlerMapping/RequestMappingHandlerAdapter) to replace the old one (DefaultAnnotationHandlerMapping/AnnotationMethodHandlerAdapter).
Some new features of Spring MVC 3.1, such as RedirectAttributes, are only supported by the new implementation.
If you use <mvc:annotation-driven> or #EnableWebMvc to enable Spring MVC, new implementation should be enabled by default. However, if you declare HandlerMapping and/or HandlerAdapter manually or use the default ones, you need to switch to the new implementation explicitly (for example, by switching to <mvc:annotation-driven>, if it doesn't break your configuration).

How model annotated methods should interact?

I would like to know how controller methods should interact with ModelAttribute annotated methods.
For example handlePage method would like to filter the list created by createList method?
Or set the id for the object created by createAnObject method?
Is it possible or ModelAttribute annotated methods are designed to attach static data to the model?
#ModelAttribute("someList")
public ArrayList<SomeList> createList() {
return new ArrayList<SomeList>(100);
}
#ModelAttribute("anObject")
public AnObject createAnObject() {
return new MyObject();
}
#RequestMapping(method=RequestMethod.GET)
public void handlePage(Model model) {
//Do some stuff to populate the model....
}
The two shouldn't really interact. #ModelAttribute, in this context, is intended for exposure of reference data, i.e. data that doesn't depend on the details of the request.
If your handler method needs to modify that data, then #ModelAttribute isn't appropriate. Instead, the handler method should explicitly add the data to the model after modifying it.

Resources