【问题标题】:Using @ModelAttribute on a method argument在方法参数上使用 @ModelAttribute
【发布时间】:2015-02-19 18:36:00
【问题描述】:

正如link 部分所述 在方法参数上使用@ModelAttribute

方法参数上的@ModelAttribute 表示该参数应该 从模型中检索。

但我观察到即使没有使用 @ModelAttribute 注释 UserDetail , userDetail 也会正确填充。这里是相关代码sn-p

<form:form id="userForm" action="path/userDetail" method="post" commandName="userDetail">

    @RequestMapping(value="/userDetail", method=RequestMethod.POST)
    public String processUserDetail(UserDetail userDetail, HttpServletRequest request, HttpServletResponse response, Locale locale) 
    {}

所以我的问题确实 spring 本身填充了处理程序方法参数中存在的项目自定义对象(在本例中为 userDetail) 即使没有注释@ModelAttribute。我相信@ModelAttribute 在渲染视图时发挥作用,但在提交春天时 如果模型中存在方法参数,则自动填充它?

【问题讨论】:

    标签: java spring-mvc


    【解决方案1】:

    即使没有注释@ModelAttribute,处理程序方法是否也能解析参数?

    是的,但是 @ModelAttribute 改变的是发生适当解析的点。框架在参数解析过程中所做的是它获取每个参数并遍历已注册的解析器列表,一旦找到支持该参数的第一个解析器,它就会解析它并移动到下一个参数。

    处理模型属性的解析器是ServletModelAttributeMethodProcessor。它被注册了两次,第一次只扫描使用@ModelAttribute 注释的参数,第二次作为列表中的最后一个,即使没有注释也解析参数。

    您可以通过查看RequestMappingHandlerAdapter 的来源来查看解析器的列表和顺序。注意ServletModelAttributeMethodProcessor的两个注册,true/false参数决定注解是否强制

    private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
    
        // Annotation-based argument resolution
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
        resolvers.add(new RequestParamMapMethodArgumentResolver());
        resolvers.add(new PathVariableMethodArgumentResolver());
        resolvers.add(new PathVariableMapMethodArgumentResolver());
        resolvers.add(new MatrixVariableMethodArgumentResolver());
        resolvers.add(new MatrixVariableMapMethodArgumentResolver());
        resolvers.add(new ServletModelAttributeMethodProcessor(false));
        resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));
        resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
        resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new RequestHeaderMapMethodArgumentResolver());
        resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
    
        // Type-based argument resolution
        resolvers.add(new ServletRequestMethodArgumentResolver());
        resolvers.add(new ServletResponseMethodArgumentResolver());
        resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));
        resolvers.add(new RedirectAttributesMethodArgumentResolver());
        resolvers.add(new ModelMethodProcessor());
        resolvers.add(new MapMethodProcessor());
        resolvers.add(new ErrorsMethodArgumentResolver());
        resolvers.add(new SessionStatusMethodArgumentResolver());
        resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
    
        // Custom arguments
        if (getCustomArgumentResolvers() != null) {
            resolvers.addAll(getCustomArgumentResolvers());
        }
    
        // Catch-all
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
        resolvers.add(new ServletModelAttributeMethodProcessor(true));
    
        return resolvers;
    }
    

    【讨论】:

    • 所以风险......它可以被其他解析器匹配 - 那可能是哪一个?
    • 我找不到示例,所以我已将其从响应中删除
    【解决方案2】:

    TLDR

    当您只关心填充您自己类型的实例时,没关系。

    加长版

    在大多数情况下,添加或省略 @ModelAttribute 并没有什么不同。正如@MasterSlave 已经提到的,在幕后使用了相同的机制。在我谈论何时使用 @ModelAttribute 很重要之前,让我先解释一下这个模型到底是什么。

    从正在呈现的视图的角度来看,它是视图可以访问的数据。在您的情况下,userDetail。控制器负责将所有必要的数据添加到模型中。有很多方法可以实现这一目标。 @ModelAttribute 就是其中之一。

    显式使用@ModelAttribute 的一个用例是定义可以访问数据的键。 @ModelAttribute("user") UserDetail user 让视图通过键 user 访问数据。否则它将是userDetail

    数据也可能已经存在控制器方法被调用之前。也许它存储在会话中或由另一种方法生成。就是这个意思

    应该从模型中检索参数

    但它可能存在于特定键下,例如“用户”。同样,您必须通过@ModelAttribute("user") 提供名称。现有数据也可以是“简单”类型,如StringDate。在这种情况下,@ModelAttribute 是从模型中检索数据所必需的。

    对于将由其他解析器处理的类型的参数也是如此,例如 MapLocale

    【讨论】:

      猜你喜欢
      • 2017-10-03
      • 1970-01-01
      • 1970-01-01
      • 2021-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-23
      • 1970-01-01
      相关资源
      最近更新 更多