I have been reading this forum for quite awhile and find it VERY useful, thank you to the contributors. I have a question that has plagded me for several weeks. And here it goes.
#RequestMapping(value="updateNote.htm",method=RequestMethod.POST)
public String updateNote(#ModelAttribute("note")NoteBean nb, BindingResult res,Model model){
daoobj.updateNote(nb.getName(),nb.getPath(), nb.getNote());
model.addAttribute("note",daoobj.getByName(nb.getName()));
return("success");
}
#RequestMapping(value="updateNote.htm",method=RequestMethod.GET)
public String updateNote(#ModelAttribute("note")NoteBean nb,Model model){
populateNoteBean();
model.addAttribute("note",daoobj.getByName(nb.getName()));
return("editNote");
}
#ModelAttribute("WHAT")
public NoteBean populateNoteBean() {
NoteBean nnb = new NoteBean();
return nnb;
}
With the method populateNoteBean() the model attribute is "WHAT". But, the name that I use is "note". So when I run the code, the NoteBean is correctly saved to the data base. My question is HOW?? It seems that the name "WHAT" should be "note" or that the model attribute is saving it as no name.
Thank for your time.
With your current code you will have two instances of your notebean in the model!
First spring invokes all modelattribute annotated methods in your controller and places the results in the model. Second it evaluates the ones from your requestmapping method.
The point of a modelattribute annotated method is that you can choose how to create your bean. Load it for example from a database.
We use this approach like that:
modelattr method (name="note")
Loads beans from db
requestmapping method with modelattr param (name="note")
Merges the note bean created by the first method with the request paramters from a submit for example and you habe directly access to the modifed one.
One nice effect:
We do not want to put hidden input fields for all attributes in a form just to be able to merge the entity with the entitymanager. This way you can have a form with only one attribute (plus one for the id to be able to fetch the entity)
Or another one:
If your note bean is an abstract class spring has no possibility to instanciate the bean because it does not know what to instanciate. You can for example add a requestparam parameter in the modelattr annotated method and decide what to do yourself.
This is very well described in the documentation. Either the reference or in the api of either controller, reqestmapping or modelattribute i believe.
Related
I have a simple form that edits my profile on the web. 'Person' bean, that describes user, contains many internal fields that cannot be changed by the form. Therefore I have just a subset of fields available for editing on the form. So far so good. Now what if some advanced user opens developer tools in Chrome browser and adds some other fields on the form or rename some existing fields ... so when submitted those fields will be bound back to the 'Person' bean and stored into database. Such way the user can spoof my form easily and chnage values for not allowed fields. Is there a way how to define (server side) which fields (bean properties) can be bound during the form submit?
Here is how the controller method signature looks like to get an idea:
#RequestMapping(path = "/profile/edit", method = RequestMethod.POST)
public String editProfile(#ModelAttribute("profile") Person doc, BindingResult result, Model m){
... saving doc to database ...
}
I'm using SpringBoot 1.3.5 with Thymeleaf ...
Turned out that solution is quite simple. I just added #InitBinder annotated method to controller and used WebDataBinder provided object to specify list of fields allowed. To do this I can use binder.setAllowedFields(...) method. Field names support "xxx*", "*xxx" and "xxx" patterns so its easy to specify set of fields when named properly in the bean. Now when post request variables are bound to my bean, these allowed fields are preserved and the others are rejected and not bound.
Code example:
#InitBinder // or #InitBinder("profile") with ModelAttribute name information
public void initBinder(WebDataBinder binder) {
binder.setAllowedFields("settings*");
}
See DataBinder docs for detailed info.
If I use Spring, which of these two methods is more correct.
Can I use the new() operator even if I use dipendency injection?.Can I mix both?
I would like to have some clarification on these concepts.
Thanks
First method:
#RequestMapping(method=RequestMethod.GET)
public String create(Model model){
model.addAttribute(new User());
return "index";
}
Second Method:
#Autowired
User user;
#RequestMapping(method=RequestMethod.GET)
public String create(Model model){
model.addAttribute(user);
return "index";
}
By using dependency injection does not mean that the use of new operator is automatically prohibited throughout your code. It's just different approaches applied to different requirements.
A web application in spring is composed of a number of collaborating beans that are instantiated by the framework and (unless overriding the default scope) are singletons. This means that they must not preserve any state since they are shared across all requests (threads). In other words if you autowire the User object (or any other model attribute), it is created on application context initialization and the same instance is given to any user request. This also means that if a request modifies the object, other requests will see the modification as well. Needless to say this is erroneous behavior in multithreaded applications because your User object (or other model attribute) belongs to the request, so it must have the very narrow scope of a method invocation, or session at most.
You can also have spring create beans with different scopes for you, but for a simple scenario of a model attribute initialization, the new operator is sufficient. See the following documentation if interested in bean scopes : Bean scopes
So in your use case, the second method is totally wrong.
But you can also delegate the creation of your model attributes to spring if they are used as command objects (i.e. if you want to bind request parameters to them). Just add it in the method signature (with or without the modelattribute annotation).
So you may also write the above code as
#RequestMapping(method=RequestMethod.GET)
public String create(#ModelAttribute User user){
return "index";
}
see also : Supported method argument types
If you want your beans to be "managed" by Spring (for e.g. to use with Dependency Injection or PropertySources or any other Spring-related functionality), then you do NOT create new objects on your own. You declare them (via XML or JavaConfig) and let Spring create and manage them.
If the beans don't need to be "managed" by Spring, you can create a new instance using new operator.
In your case, is this particular object - User - used anywhere else in code? Is it being injected into any other Spring bean? Or is any other Spring bean being injected in User? How about any other Spring-based functionality?
If the answer to all these questions is "No", then you can use the first method (create a new object and return it). As soon as the create() method execution is complete, the User object created there would go out of scope and will be marked for GC. The User object created in this method will eventually be GC-ed.
Things can be injected in two ways in a Spring MVC applications. And yes, you can you can mix injection and creation if doing right.
Components like the controller in your example are singletons managed by the application context. If you inject anything to them it is global, not per request or session! So a user is not the right thing to inject, a user directory can be. Be aware of this as you are writing a multithreaded application!
Request related things can be injected to the method like the used locale, the request, the user principal may be injected as parameters, see a full list at Spring MVC Documentation.
But if you create a model attribute you may use new() to create it from scratch. I will not be filled by spring but to be used by your view to display data created by the controller. When created in the request mapped method that is ok.
What are the advantages of using #ModelAttribute and #Valid?
Which are the differences?
Is it possible to use them together?
#ModelAttribute is used to map/bind a a method parameter or method return type to a named model attribute. See #ModelAttributes JavaDoc. This is a Spring annotation.
#Valid is an annotation that marks an object for JSR-303 bean validation. See #Valids JavaDoc. It is part of JavaEE 6, but I think Hibernate has an earlier implementation that most people use instead.
The advantage of using #ModelAttribute is that you can map a form's inputs to a bean. The advantage of #Valid is that you can utilize JSR-303 bean validation to ensure that the bean that is made is validated against some set of rules.
Yes you can use #ModelAttribute and #Valid together.
The best way to transfer data from a form (sic View) to the a Model object is to follow the typical/traditional MVC design pattern using Spring. My personal preferred way is to have a form in a JSP with Spring JSTL <form:*> tags, with a modelAttribute set. On the Controller, have a handler to accept the POST from the form that has a matching #ModelAttribute that is a bean that represents the form's input. I would then pass that "Form Bean" to a service layer to do some things, including translating the "Form Bean" into any models if needed (not necessary if the form is creating your model objects directly) and saving/updating/etc via a DAO. This is but one way to do things, but it's probably the bulk of what I do with Spring in my day-to-day job.
I would highly recommend reading the Spring reference materials and following the tutorials. The reference materials are very well written, easy to follow, and includes lots of examples on the various ways you can do things in Spring, and there are usually quite a few options on how you do things in Spring.
please check the below part fro spring reference documentation:
In addition to data binding you can also invoke validation using your own custom validator passing the same BindingResult that was used to record data binding errors. That allows for data binding and validation errors to be accumulated in one place and subsequently reported back to the user:
#RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(#ModelAttribute("pet") Pet pet, BindingResult result) {
new PetValidator().validate(pet, result);
if (result.hasErrors()) {
return "petForm";
}
// ...
}
Or you can have validation invoked automatically by adding the JSR-303 #Valid annotation:
#RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(#Valid #ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
I have a controller that is 1600 lines long. This is mostly populated with a bunch of public methods with the #ModelAttribute annotation. It also has a few #RequestMapping methods.
I would like to bring down the line count and break-up this class. How do you handle multiple public methods with #ModelAttribute annotation? Aren't they all invoked whenever a request is processed?
I have a controller that is 1600 lines long
Gulp.
How do you handle multiple public methods with #ModelAttribute annotation? Aren't they all invoked whenever a request is processed?
When used to annotate a method, this annotation indicates that the method's return value should be used to populate the model for every request executed by that controller class, regardless of which #RequestMapping method is executed.
My suggestion is that you perform an audit to see which views (e.g. JSPs) use which model data provided by the various #ModelAttribute methods. It's likely that each view only uses a subset of that data.
Once you've figured out which combinations of #ModelAttribute and #RequestMapping methods go together, then break those up into individual classes.
If that doesn't fly (maybe all of the views really do use all of the data), then consider extracting the #ModelAttribute methods out of the class altogether, and stitch them together using a single method which amalgamates their outputs together manually (e.g. pass the Model or ModelMap object from the #RequestMapping method to this new method, which then adds the bits of model to that object.
Remember, #ModelAttribute-annotated methods are just a convenient way to add extra model data. They're not the only way.
#ModelAttribute methods can also return void:
#ModelAttribute
public void populateModel(Model model) {
model.addAttribute("key", "value");
// keep adding any number of attributes...
}
Can't you group multiple #ModelAttributes together? For example, if you have three methods one each for retrieving the values of three different select boxes, you could perhaps put them all into one method.
I am trying to share data between two controllers in a Spring mvc application.
In Controller A I have annotated the class with #SessionAttributes({"mymodel1"}) and in the method which receives the first GET request I add it to the ModelMap:
model.addAttribute("mymodel1", MyModel1);
I now want to read myModel1 from Controller B.
In this Controller I have the following method which intercepts the POST requests and already has a different model in its parameters:
public String processSubmit(#ModelAttribute("mymodel2") MyModel2 mymodel2, BindingResult result, SessionStatus status, HttpServletRequest httpRequest)
Up to this point everything works fine and I am able to read mymodel2 from processSubmit however if I now want to add another #ModelAttribute("mymodel1") MyModel1 mymodel1 to this method signature I would have expected to be able to read the value I was setting in Controller A however I'm getting exceptions that the first model is no longer recognised.
So my question is: how can I read mymodel2 from Controller B?
You can't do that with #SessionAttributes :
Session attributes as indicated using this annotation correspond to a specific handlers model attributes, getting transparently stored in a conversational session. Those attributes will be removed once the handler indicates completion of its conversational session. Therefore, use this facility for such conversational attributes which are supposed to be stored in the session temporarily during the course of a specific handlers conversation.
For example I use this annotation when I want to validate elements with Hibernate validation, and after I submit the page and SOME elements are invalid I want the rest to be still on the page, but this is not your case. I think that the only way to do it would be with:
HttpSession.getAttribute()
The javadoc excerpt above is the most typical way #SessionAttributes is used. However, what Joly is describing should also work. Session attributes are stored via DefaultSessionAttributeStore, which by default does not prefix attribute names when it stores them in the session. That means if ControllerA and ControllerB both list an attribute called "mymodel1", they're actually referring to the same session attribute. You'll need to provide a little more information on the error you're getting and the actual controller code.