问题
I have a lot of Spring RestControllers with methods annotated with RequestMapping
. I now would like to inject a custom object into these RequestMapping
methods, and create an custom instance for each request.
I would like to write something like the following:
@RequestMapping("/search")
public SomeReturnObject foobar(@RequestParam("query") String query, MyRequestFoo foo) {
// ...
}
Now I would like to create a mechanism, where each call to that method (i.e. each request) get a new instance of MyRequestFoo
created and injected into the method. If this would work better with an parameter annotation instead of injecting by type, that would also be okay (e.g. @MyRequestInject MyRequestFoo foo
).
I need to know if I can create now a method that creates a new instance of MyRequestFoo
especially for that request, like the following:
public MyRequestFoo createRequestInstanceSomehow(HttpServletRequest request) {
// extract some values from the HttpServletRequest and create a
// new MyRequestFoo instance from that and return it
}
Is this possible by any means to create such a mechanism, so that I can inject custom per request objects into my request handling methods?
回答1:
Spring MVC has a arguments resolver construct that directly supports your request. Every handler method annotated with @RequestMapping will be subject to argument resolving, where the framework scans through the handler arguments, checks the type and instantiates an appropriate object. That is the mechanism behind injecting request, model and a number of other types, just by declaring the object in the handler's method signature.
You can write a custom argument resolver to have the custom types resolved and available in the method. The procedure is simple three step process
Make a POJO class, in your case MyRequestFoo
Make a resolver, e.g.
public class MyRequestFooResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType().equals(MyRequestFoo.class); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { return new MyRequestFoo(); } }
3.Register a resolver
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="your.package.MyRequestFooResolver "></bean>
</mvc:argument-resolvers>
</mvc:annotation-driven>
or in java config
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(List< Handlermethodargumentresolver > argumentResolvers) {
MyRequestFooResolver myRequestFooResolver = new MyRequestFooResolver ();
argumentResolvers.add(myRequestFooResolver );
}
}
Than you use it just by adding the type as a handler method argument
@RequestMapping("/search")
public SomeReturnObject search(MyRequestFoo foo) {
// ...
}
回答2:
What about putting an instance variable of type MyRequestFoo on the Controller class and Autowire it changing the default scope from "Singleton" to "Request" on the Bean definition?
Check out this link or the Spring reference sheet!
回答3:
I found a solution, that does what I was trying to do.
Just create the MyRequestFoo
as a bean with scope "request" and you can access the current request via the RequestContextHolder
:
@Component
@Scope("request")
public class MyRequestFoo {
private final HttpServletRequest request;
public MyRequestFoo() {
request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
}
// do whatever you want with the request
}
And now i can just inject a new instance of this by its type into any request handler method:
@RequestMapping("/search")
public SomeReturnObject search(MyRequestFoo foo) {
// ...
}
And Spring will automatically take care of instantiating a new instance.
It does not work to autowire MyRequestFoo
as an instance variable, since you cannot autowire request scoped beans into non request scoped beans.
回答4:
Since you need to pass in request-specific information to your bean, I recommend instead injecting a builder bean into your controller and calling builder.build(request)
from your method to create the new per-request instance.
来源:https://stackoverflow.com/questions/29250080/inject-a-new-instance-of-an-object-into-each-request-handler