We are working on a Spring 3.0.5 Web MVC-based application. In our code we quite often do something like this:
#ModelAttribute(ModelKeys.SOME_BEAN)
public SomeBean newSomeBean() {
return new SomeBean();
}
I think this is not necessary. But if it really wasn't, then I wonder how has this managed to slip through so many code reviews? In my understanding, if a controller method wanted a NEW SomeBean, then annotating a parameter in that method with #ModelAttribute should be enough? Spring would then use the default constructor to new up the required bean for invoking the controller method, e.g.:
#RequestMapping(method = RequestMethod.POST)
public String doIt(
#ModelAttribute(ModelKeys.SOME_BEAN) final SomeBean bean,
final BindingResult bindingResult)
{
...
}
here, Spring would new up an instance of SomeBean and try to data-bind into it from the POSTed data, right? There's no need for the method as shown in the first code snippet? Please can you confirm this or provide me with your thoughts on this? Would I be introducing a risk if I just went ahead and removed all these methods that do nothing other than new up an empty bean?
#ModelAttribute annotation on a method does not bind the bean attributes with HTTP request parameters. This is the key difference from the same annotation on a method parameter. Such approach is useful to populate some data that does not depend on request parameters in the model, for instance, values of a comboboxes taken from dictionaries. This is especially helpful, if you have several handler methods in a controller, e.g. to view/change/delete the same type of object and you need the same set of model attributes in all of them.
Related
I am new to Spring and have a project where I am trying to inject something like the following beans:
ProductHandle selectedProduct
List<ProductHandle > allProducts
When I try to have a List<ProductHandle> autowired into another class, it injects a List containing selectedProduct, rather than the list I actually want. This is even though the name of the autowired property is called allProducts, so I thought this would identify the correct bean.
How would I go about achieving what I want?
--
Edited to show that it isn't just a case with Strings - this is a more generic and hopefully less offensive case..
--
Note beans are being registered as so, since the values are being parsed from command line args.
mySelectedProduct is a ProductHandle
allMyProducts is a List<ProductHandle>
try (final AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext()) {
ctx.registerBeanDefinition("selectedProducts", BeanDefinitionBuilder
.rootBeanDefinition(ProductHandle.class, "create")
.addConstructorArgValue(mySelectedProduct)
.getBeanDefinition());
ctx.registerBeanDefinition("allProducts", BeanDefinitionBuilder
.rootBeanDefinition(Lists.class, "asList")
.addConstructorArgValue(allMyProducts)
.getBeanDefinition());
ctx.refresh();
final MyApp app = ctx.getBean(MyApp.class);
app.run();
}
Using standard data structures as Spring beans is an antipattern, don't do it. (Read here about "stringly typed" software)
Either inject the list as a #Value, or wrap it in a custom object that expresses the intent.
Regarding your updated question:
If you have a bean of type X, and register a dependency of type List<X>, Spring will inject a list of all dependencies of the supplied type.
While this is a very handy feature, it's also one more reason never to define standard data structures (like Lists) as Spring Bean. Wrap your list in a custom object and you should be fine.
Also: ProductHandle sounds like it's a business object. Usually, you should not let Spring manage these, but rather the services that act on business objects. Typically, Spring would know about a ProductRepository, or a ProductService, but not a Product or ProductHandle.
The web layer is a notable exception to this rule, but even there, business objects should be method parameters, not Spring beans.
Is it possible to create a Java annotations equivalent to a Spring MVC #RequestMapping with a predefined set of parameters?
For instance, something that allows me to simply use:
#PostJson("/input")
public Output myMethod(Input input) {
instead of:
#RequestMapping(value = "/input",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public Output myMethod(Input input) {
UPDATE:
Let me try to dig a little deeper. AFAIK, Spring seems to be able to handle meta-annotations when scanning for beans. For instance, if I create an annotation, lets say #MyComponent, and annotate it with #Component:
#Component
public #interface MyComponent {
String value() default "";
}
Spring seems able to find beans with #MyComponent and to recognize the parameters (value in this case) in #MyComponent as if they were from #Component
#MyComponent("MyBean")
public class SomeClass {
I've tryed similar tactic with #RequestMapping
#RequestMapping(method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
public #interface PostJson {
String value() default "";
}
The fixed parameters (method an produces) seems to be correct, nonetheless, the variable parameter (value) is ignored.
I am hoping that this is not a #Component specific feature and that I could use it with #RequestMapping.
Not easily, no. The #RequestMapping annotation is tied to RequestMappingHandlerMapping and RequestMappingHandlerAdapter. These are two elements of the MVC stack registered by default when you provide <mvc:annotation-driven /> or #EnabledWebMvc. They only scan for #Controller and #RequestMapping, nothing else.
To make your own annotation work, you'd have to override (and register) these two or create new ones from scratch which provide the scanning and handling of handler methods. You can get some inspiration from those classes and other HandlerMapping implementations, but it really isn't a simple task.
You might, alternatively, want to look into Java Restful Web Services which can integrate quite well with Spring (not necessarily Spring MVC though). It can provide some less bloated mapping annotations when you know exactly what you want.
While this isn't currently supported out of the box but will be (thanks for creating https://jira.spring.io/browse/SPR-12296!), it isn't hard to do. If you look in RequestMappingHandlerMapping the protected method getMappingForMethod accepts a method and returns a RequestMappingInfo populated with information from the type + method-level #RequestMapping annotations. You can however populate this RequestMappingInfo from anything, e.g. your own annotation, or some other source (external routes configuration). Just follow the example of the code there.
Say I have the following class...
#Controller
public class WebController {
#Autowired PersonService personService;
#RequestMapping(value = "/get", method = RequestMethod.GET)
#ResponseBody
#Scope("session")
public List<Player> getPerson(String personName) {
return playerService.getByName(personName);
}
}
Now this invokes the following service...
#Service("playerService")
public class PlayerServiceImpl implements PlayerService {
private List<Player> players;
#Override
#Transactional
public List<Player> getByName(final String name) {
if (players == null) {
players = getAll();
}
return getValidPlayers(name);
}
If I initially start my application, players is null, correctly, then when in the same session, I invoke this method again with a new value, players is no longer null, as you would expect. However, no new thread appears to be being created, if I open a new browser window (therefore creating a new session) and invoke this method, it still has the values from the previous session.
Why is #Scope("session") not creating a new thread in the thread pool?
I've specified <context:component-scan base-package="com." /> in my servlet-context as expected, everything works fine apart from the service methods are all acting as singletons rather than creating a new thread per session like say a Java EE container.
If players was marked as static I'd understand.
I've also tried marking my controller as #Scope("session") (as shown below) but this appears to have no impact either. What's the best way to make my Spring app create a new thread for a new session?
#Controller
#Scope("session")
public class PlayerController {
You are using #Scope annotation the wrong way.
Quoting the docs:
When used as a type-level annotation in conjunction with the Component annotation, indicates the name of a scope to use for instances of the annotated type.
When used as a method-level annotation in conjunction with the Bean annotation, indicates the name of a scope to use for the instance returned from the method.
So you can annotate either a spring component bean or a method that creates a bean if you're using java config. Java config is the only reason it even compiles (it wouldn't in pre 3.0 spring)
In your case that annotation is on a normal bean method where it doesn't mean anything.
Solving the right problem
It looks like you're trying to implement db cache by storing query results in a List<Player> players.
Don't do that. Use one of the prebuilt cache abstractions (spring has a very nice one) instead.
So where should #Scope go?
Annotating #Controller with #Scope("session") won't help as it will create session scoped controllers but the service they have injected is still a singleton.
Annotating only Service bean won't work either, cause #Controller is a singleton and it's dependencies are autowired on application startup.
Annotating both #Service and #Controller might work, but seems a bit heavy handed.
It's better to avoid state at all.
New threads are created for each request.
Your service has an instance variable (players) which is not threadsafe - it is shared by all threads. Any spring bean - including controllers and services are by default a singleton, you need to specify on the service annotation its scope.
#Service("playerService")
#Scope("session")
public class PlayerServiceImpl
But its best(simpler, easier to scale) to keep beans singletons and not rely on instance variables (unless they are also managed by spring/threadsafe/singletons).
I'm trying to protect a Controller with the #PreAuthorize annotation at type level and try to override that behavior by annotating some methods with a different #PreAuthorize. The Problem is however, that Spring is evaluating the method annotation first (grants access) and is then evaluating the class annotation (denies access).
Is there any way to reverse that order? I couldn't figure it out yet.
Edit:
On the method level, I want to grant access to non-registered Users only:
#PreAuthorize("isAnonymous()")
#RequestMapping(value = "/create", method = RequestMethod.GET)
public String renderCreateEntity(ModelMap model) {
return userService.renderCreateEntity(model);
}
The standard for this Controller however, should be to allow fully authenticated users only:
#Controller
#RequestMapping(value = "/user")
#PreAuthorize("isFullyAuthenticated()")
public class UserController { [...] }
When debug-stepping through the app, I see that isAnonymous() is evaluated first and then isFullyAuthenticated() thus resulting in an grant of access right and immediately denying access again.
Thanks for all your replys.
The answer however, was something totally different :)
I put this here in case anyone else has the same problems.
I registered a custom validator in an #InitBinder annotated method. This binding method is called AFTER the method call requested on the controller. And since this binding method was not annotated with #PreAuthorize, the request was denied.
The solution was to annotate the binding method like this:
#InitBinder
#PreAuthorize("permitAll")
public void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
And then, the method calls from my OP evaluated like expected.
The problem is not that you need to change the order of grant and deny. The problem is simple that that method level annotations override the class level annotations.
PrePostAnnotationSecurityMetadataSource Java Doc:
Annotations may be specified on classes or methods, and method-specific annotations will take precedence.
The concrete implementation of this logic is done in the method findAnnotation of class PrePostAnnotationSecurityMetadataSource. (Unfortunately this method is private.)
So you can write your own MethodSecurityMetadataSource, if you have a look at the code of PrePostAnnotationSecurityMetadataSource, you will see how easy it is.
But one warning at the end: the end: difficult task is not rewriting the method, the difficult task is to "inject" the new MethodSecurityMetadataSource into the security system. I belive you can not do it with the spring security namespace configuration, so you need to replace spring security namespace by explicit bean declaration.
I have some serious doubts regarding scope bean dependencies because of lack of spring knowledge.
I have read reference manual at 3.5.4.5 Scoped beans as dependencies and have implemented sucessfully an example about it.
However before going further more, I wanted to share my concerns.
Let me to share my use case and little implementation details
For each user request I would like to create a city for per user.
#Configuration
public class CityFactory{
#Bean(name = {"currentCity" , "loggedInCity"})
#Scope(value = WebApplicationContext.SCOPE_REQUEST,proxyMode = ScopedProxyMode.TARGET_CLASS)
#Autowired
public CityBean getCityBean(HttpServletRequest request) {
return CityUtil.findCityWithHostName(request.getServerName());
}
For each request I want to inject this city into a singleton scoped controller which is default scope for controller.
#RequestMapping("/demo")
#Controller
public class DemoController {
#Autowired
CityBean city;
#RequestMapping(value = "/hello/{name}", method = RequestMethod.GET)
public ModelAndView helloWorld(#PathVariable("name") String name, Model model) {
Map<String, Object> myModel = new HashMap<String, Object>();
model.addAttribute("hello", name);
model.addAttribute("test", "test in " + city.getDomainName() + " !!! ");
return new ModelAndView("v3/test", "m", model);
}
}
My questions:
1) Is there any race condition? I am afraid of context switches which will destroy my application in a multi request environment.
2) I am aware of another solution which creating a controller per request but it is more error prone than current solution. Because another developer can forget scoping controllers to make request.
How can I make controllers globally request scope? Just because of being little curious.
Thanks...
No race conditions - each request has its own thread
But I think there's an easier way to do what you want. You can have your CityBean:
#Service
public class CityBean {
public String getDomainName(String serverName) {
// obtain the name based on the server name
}
}
And in your controller:
#Autowired CityBean bean
pass HttpServletRequest as argument to the method, and call cityBean.getDomainName(request.getServerName());
(If you use some ORM, perhaps you'll have a City entity, which you can fetch and pass around, just beware of lazy collections)
There are no race conditions here.
That's the point of scoped proxies - the instance of CityBean injected into DemoController is a proxy which delegates calls of its method to the actual request-bound instance of CityBean, so that each request works with its own CityBean.
I agree that you shouldn't make the controller itself request-scoped - it would be confusing for other people since it's not a typical approach in Spring MVC applications.
You can also follow the approach suggest by Bozho and get rid of request-scoped bean at all, though that approach has a drawback since it requires you to add extra argument to your controller methods.