如何在没有任何显式引用的情况下注入@ModelAttribute

时间:2014-05-09 13:11:45

标签: java spring spring-mvc

我有一个有效的控制器,但我不明白为什么它有效。这是:

    @Controller
    @RequestMapping("/someUrl")
    public class MyController {
        @Autowired
        private SomeService someService;

        @RequestMapping(method = RequestMethod.GET)
        public String page() throws ApplicationException {
            return "page";
        }

        @ModelAttribute("modelAttribute")
        public PageModel loadPageModel() throws ApplicationException {
            return someService.createPageModel();
        }
    }

访问“/ someUrl”会让我看到名称“page”的视图,正如预期的那样。令人费解的是,尽管模型属性为“modelAttribute”,或者类型“PageModel”的对象未在方法页面附近的任何位置引用,但对于视图仍然可以看到modelAttribute。我很高兴这有效,但我不明白为什么。有什么想法吗?

3 个答案:

答案 0 :(得分:0)

正如contract for the ModelAttribute annotation所说:

将(...)方法返回值绑定到指定模型属性的注释,公开给Web视图。 支持使用@RequestMapping方法的控制器类。

换句话说,您通过返回隐含地添加属性。

为什么会这样?

我不知道Spring内部是如何做到的,但我们知道的是我们如何才能让它有资格让Spring MVC识别并使其可用于well documented视图:

  1. 拥有 @RequestMapping 控制器方法(在您的情况下,在类级隐式定义)
  2. supported return types中的一个与ModelAttribute注释结合使用。
  3. 当然属于组件扫描区域

答案 1 :(得分:0)

仔细查看ModelAttributeMethodProcessor并查找handleReturnValue方法。此方法将在使用@ModelAttribute注释的控制器方法上执行。

/**
 * Add non-null return values to the {@link ModelAndViewContainer}.
 */
@Override
public void handleReturnValue(
        Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
        throws Exception {

    if (returnValue != null) {
        String name = ModelFactory.getNameForReturnValue(returnValue, returnType);
        mavContainer.addAttribute(name, returnValue);
    }
}

您可以看到它只是将它的返回值添加到模型中,并且会针对该特定控制器的每个请求调用它。

P.S。大多数“magic”操作的起点可以在RequestMappingHandlerAdapter中找到,这是现代Spring MVC应用程序的默认HandlerAdapter

答案 2 :(得分:0)

@ModelAttribute有点过载,根据声明的位置提供两个主要功能。

使用方法参数:

@RequestMapping(value="/create", method=POST)
public String createPage(@ModelAttribute("pageModel") PageModel pageModel) ...

例如,当使用方法参数声明@ModelAttribute并向该方法提交参数时,Spring将尝试将提交的参数绑定到类型为PageModel的对象。它在哪里得到PageModel:1)如果有一个当前在Spring托管模型中,使用它2)否则它将使用PageModel对象的默认构造函数创建一个PageModel

在方法声明中(您如何使用它):

@ModelAttribute("modelAttribute")
public PageModel loadPageModel() throws ApplicationException {
   return someService.createPageModel();
}

在您的情况下,当在方法级别声明@ModelAttribute时,它告诉Spring该方法将在为特定请求调用任何RequestMapping之前提供PageModel。创建的PageModel实例将放入" modelAttribute"下的Springs托管模型中。键。然后从您的视图中,您应该能够引用该PageModel。

所以请求是这样的(我只显示相关事件):

  1. 请求/ someUrl
  2. Spring将请求发送到page()方法
  3. Spring看到那个page()方法用@RequestMapping注释,所以它在执行page()之前调用所有用@ModelAttribute注释的方法(在方法级别)
  4. 重定向到"页面"视图,其中PageModel位于" modelAttribute"密钥可供您使用
  5. 注意:

    • 如果你的loadPageModel()方法没有返回PageModel,它仍会被调用。
    • 如果在方法级别使用@ModelAttribute声明了多个方法,则每次执行带有@RequestMapping的方法时都会调用它们