I have this class:
@Component
@Scope("session")
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue
@GenericGenerator(name = "incremental", strategy = "increment")
private Long userID;
@Column(nullable = false)
private String username;
@Column(nullable = false)
private String email;
@Column(nullable = false)
private String password;
// getters and setters
}
And this controller:
@Controller
@SessionAttributes("user")
@Scope("request")
public class UserCreationWizard {
@Autowired
private User user;
@ModelAttribute("user")
private User createUser() {
return user;
}
@RequestMapping(value = "/new/users/page/", method = RequestMethod.GET)
public String begin(HttpServletRequest request) {
return "wizard";
}
@RequestMapping(value = "/new/users/page/{page}", method = RequestMethod.POST)
public String step(@ModelAttribute("user") User user,
@RequestParam("username") String username,
@RequestParam("email") String password,
@PathVariable() Integer page) {
return "wizard" + page;
}
@RequestMapping(value = "/new/users/page/end", params = "submit", method = RequestMethod.POST)
public String end(@RequestParam("password") String password) {
user.setPassword(password);
user.setActive(true);
user.setLastLoggedIn(Calendar.getInstance());
Session s = HibernateUtils.getSessionFactory().openSession();
Transaction t = s.beginTransaction();
try {
s.persist(user);
s.flush();
t.commit();
s.close();
} catch (HibernateException e) {
t.rollback();
}
return "wizard";
}
}
begin() just loads the first view (a jsp) in the user creation wizard. It has input fields for username and email. In the view, you make a POST form submission which triggers step(). In the second view (wizard+page.jsp), you have a password field and a submit input that triggers end().
- In debug mode I noticed that in
step(), where I’ve passed User as
a ModelAttribute, I don’t need to set its fields for username and
password. They are automatically grabbed from the RequestParams
attributes. Inend()however, where I don’t have a ModelAttribute,
I have to set the password manually. How does Spring manage this? - Also if I take out the
createUser()method in the Controller, the
application fails saying it couldn’t find a session attribute for
“user”. How is this method linked to MethodAttribute as a method
parameter? - Lastly, if I take out @SessionAttributes, the application doesn’t fail but I feel like something is going wrong. Will the User user now be global to all httprequests?
My general question is: are spring beans mapped to their name? Eg. Here I have ‘user’ as a user and ‘user’ in the session, ‘password’ as a requestparam and ‘password’ as the User member variable.
Ok, many questions. Let’s see, all references are to the documentation for the current Spring MVC version.
1) The behaviour you see in the
userattribute is explained in the section “Using @ModelAttribute on a method argument“How does Spring does this? Well, the source code is the ultimate answer but it’s not that hard to make a guess: Spring knows the argument is an instance of
Userand via reflection it can read the methods of the class, particularly its setters. In this case it findssetUsername()andsetEmail()and the argument for those methods is aStringso it’s compatible with the parameters from the request.(BTW:
@RequestParam("email") String passwordis possibly a mistake. At the very least is confusing)2) The method
createUser()is preceded by the annotation@ModelAttribute("user"). This is covered by the section “Using @ModelAttribute on a method“Therefore, this method puts an object associated with the name
"user"on the model and is then available to be used as a parameter by other methods, likestep(). Notice that the annotation controls the identifier used by the object in the model. If you change the code tothe app will break. But it will work again if you change the
step()signature to3) The process described in 1) and 2) stores objects in the model during a request. With the class annotation
@SessionAttributes("user")you extend the life of the object adding it to the currentSessionor something equivalent. By example you could then use the object in otherControllers in the same was as thestep()method.Finally just to be clear
Hope this manages to be more clear than the official documentation whch, is usually too brief, I’ll give you that.