解释这个Spring MVC Controller的行为

时间:2012-11-22 22:30:39

标签: java spring spring-mvc dependency-injection

我有这堂课:

@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
}

这个控制器:

@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()只是在用户创建向导中加载第一个视图(jsp)。它包含usernameemail的输入字段。在视图中,您进行POST表单提交,触发step()。在第二个视图(向导+ page.jsp)中,您有一个password字段和一个触发end()的提交输入。

  1. 在调试模式下,我注意到在step()中,我已经将User作为 一个ModelAttribute,我不需要为username和。设置它的字段 密码。它们会自动从RequestParams中获取 属性。但是在end()中,我没有ModelAttribute, 我必须手动设置密码。 Spring如何管理这个?
  2. 如果我在Controller中取出createUser()方法,那么 应用程序失败,说它无法找到会话属性 “用户”。该方法如何作为方法链接到MethodAttribute 参数β
  3. 最后,如果我拿出@SessionAttributes,应用程序不会失败,但我觉得有些事情出错了。用户用户现在是否对所有httprequests都是全局的?
  4. 我的一般问题是:春豆是否映射到他们的名字?例如。在这里,我在会话中将'user'作为用户和'user',将password作为requestparam,将'password'作为User成员变量。

1 个答案:

答案 0 :(得分:1)

好的,很多问题。让我们看看,所有引用都是针对当前Spring MVC版本的文档。

1)您在user属性中看到的行为在“Using @ModelAttribute on a method argument”部分进行了解释

  

方法参数上的@ModelAttribute指示参数应该   从模型中检索。如果模型中没有,则参数   应首先实例化,然后添加到模型中。一旦到场   在模型中,参数的字段应该从所有字段中填充   请求具有匹配名称的参数。这称为数据   Spring MVC中的绑定,一种非常有用的机制,可以帮助您避免使用   必须单独解析每个表单字段。

Spring是如何做到的?好吧,源代码是最终的答案,但并不难猜测:Spring知道参数是User的一个实例,通过反射它可以读取类的方法,特别是它的 setters 。在这种情况下,它会找到setUsername()setEmail(),这些方法的参数是String,因此它与请求中的参数兼容。

(顺便说一下:@RequestParam("email") String password可能是个错误。至少令人困惑)

2)方法createUser()前面是注释@ModelAttribute("user")。这由“Using @ModelAttribute on a method

部分涵盖
  

方法上的@ModelAttribute指示该方法的用途   添加一个或多个模型属性。

因此,此方法将与名称"user"关联的对象放在模型上,然后可以通过其他方法(如step())用作参数。请注意,注释控制模型中对象使用的标识符。如果您将代码更改为

@ModelAttribute("strangeWeirdIdentifier")
private User createUser() { return user; }

应用程序将中断。但如果您将step()签名更改为

,它将再次起作用
public String step(@ModelAttribute("strangeWeirdIdentifier") User user,
                   @RequestParam("username") String username,
                   @RequestParam("email") String password,
                   @PathVariable() Integer page) {

3)1)和2)中描述的过程在请求期间将对象存储在模型中。使用类注释@SessionAttributes("user")可以延长对象的生命周期,将其添加到当前Session或等效的内容中。例如,您可以使用与Controller方法相同的其他step()中的对象。

最后要明确

  • 问题2中的注释发生在问题1中的用法之前。
  • 可能不需要问题3中的注释
  • Spring不会将bean映射到Java代码中的名称,而是映射到注释中使用的名称。为了清楚起见,重复与示例中相同的名称并不罕见。

希望这比官方文档更清晰,通常太简短,我会给你。