Spring中的@ModelAttribute模型属性绑定详解

 更新时间:2024年02月14日 09:00:00   作者:securitit  
这篇文章主要介绍了Spring中的@ModelAttribute模型属性绑定详解,@ModelAttribute用于将方法参数或返回值绑定到Model属性上,并公开给Web视图,支持使用@RequestMapping注释的Controller类,需要的朋友可以参考下

前言

@ModelAttribute用于将方法参数或返回值绑定到Model属性上,并公开给Web视图。

支持使用@RequestMapping注释的Controller类。

注解解析

① value:

待绑定到Model属性的键名称。

默认Model属性键名称根据非限定类名从声明的属性类型(即方法参数类型或方法返回类型)推断而来:

mypackage.OrderAddress类型对应的键名称是orderAddress 。

List<mypackage.OrderAddress>类型对应的键名称是orderAddressList。

② name:

绑定的参数名称,参数值为String类型。name和value可以同时使用,但两者的值需一致,否则会出现错误。

attribute 'name' and its alias 'value' are present with values of [XXX] and [XXX], but only one is permitted

③ binding:

允许直接在@ModelAttribute注释的方法参数或@ModelAttribute注释的方法返回值上声明禁用数据绑定,这两种方法都将阻止数据绑定。

默认情况下,binding被设置为true,此时将正常进行数据绑定。将binding设置为false,将禁用数据绑定。

注解应用

1) @ModelAttribute注释方法

@ModelAttribute注释方法将在Controller中@RequestMapping注释方法前被调用。对于@ModelAttribute注释方法有以下几种情况:

① @ModelAttribute注释void返回类型方法

此种情况下,由于方法返回值为void类型,并不会做其他处理。因此若需要向ModeMap中添加属性,需要通过方法参数ModelMap来完成。

@ModelAttribute
public void modelAttributeWithVoidMethod(ModelMap modelMap) {
    modelMap.addAttribute("modelAttributeWithVoidMethod",
                          "@ModelAttribute注释在void返回类型的方法上.");
}

② @ModelAttribute注释非void返回类型方法

此种情况下,①中所使用操作ModelMap方式仍然有效,需要通过方法参数ModelMap来添加属性。

同时,以返回值类型推断出的键、返回值作为键值对添加到ModelMap中。

@ModelAttribute
public String modelAttributeWithStringMethod() {
    return "@ModelAttribute注释String返回类型方法,会自动将返回值添加到ModelMap中,键根据返回类型生成.";
}

③ @ModelAttribute注释非void返回类型方法,并指定其name或value属性

​此种情况下,①中所使用操作ModelMap方式仍然有效,需要通过方法参数ModelMap来添加属性。

同时,以@ModelAttribute注解name或value属性值、返回值作为键值对添加到ModelMap中。

@ModelAttribute("defModelAttributeName")
public String modelAttributeWithStringMethodDefName(ModelMap modelMap) {
    return "@ModelAttribute注释String类型返回值方法,会自动将返回值添加到ModelMap中,键是@ModelAttribute的name或value属性值.";
}

④ @ModelAttribute与@RequestMapping注释同一方法

此种情况下,@ModelAttribute与@RequestMapping相互作用,会使@RequestMapping表现出稍许差异。@ModelAttribute会使得@RequestMapping注释方法的某些类型返回值不会使用对应HandlerMethodReturnValueHandler,而是由ModelAttributeMethodProcessor解析。

ModelAttributeMethodProcessor会针对返回值与②或③中进行同样处理。视图名称由RequestToViewNameTranslator根据请求/ModelAttributeWithRequestMapping.do转换为逻辑视图ModelAttributeWithRequestMapping。

不受影响的返回类型包括:ModelAndView、 Model、 View、ResponseBodyEmitter、StreamingResponseBody、HttpEntity、HttpHeaders、Callable、DeferredResult、AsyncTask。

@ModelAttribute
@RequestMapping(
    value = "/ModelAttributeWithRequestMapping.do",
    method = RequestMethod.GET)
public String modelAttributeWithRequestMapping(ModelMap modelMap) throws Exception {
    logger.info("@ModelAttribute与@RequestMapping共同作用在一个方法.");
    return "webannotations/ModelAttribute";
}

2) @ModelAttribute注释参数

① @ModelAttribute注释方法参数

此种情况下,@ModelAttribute注解用于ModelMap数据映射到控制器处理方法的参数中。

@RequestMapping(
    value = "/ModelAttributeParameters.do",
    method = RequestMethod.GET)
public ModelAndView modelAttributeParameters(@ModelAttribute("defModelAttributeName") String modelAttr,
                                             ModelMap modelMap) throws Exception {
    logger.info("@ModelAttribute注释方法参数,从ModelMap中取值:[key=defModelAttributeName, value=" + modelAttr + "].");
    return new ModelAndView("webannotations/ModelAttribute", modelMap);
}

注解示例

1) 建Controller,用来演示@ModelAttribute使用方法。

package com.arhorchin.securitit.webannotations;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author Securitit.
 * @note 演示@ModelAttribute使用方法.
 */
@Controller
@RequestMapping("/WebAnnotations")
public class ModelAttributeController {

    /**
     * logger.
     */
    private Logger logger = LoggerFactory.getLogger(ModelAttributeController.class);

    /**
     * @ModelAttribute注释
     *  void类型返回值方法. 
     *  需要手动ModelMap中添加数据.
     */
    @ModelAttribute
    public void modelAttributeWithVoidMethod(ModelMap modelMap) {
        modelMap.addAttribute("modelAttributeWithVoidMethod",
                "@ModelAttribute注释在void返回类型的方法上.");
    }

    /**
     * @ModelAttribute注释
     *  String返回类型方法. 
     *  会自动将返回值添加到ModelMap中,键根据返回类型生成.
     */
    @ModelAttribute
    public String modelAttributeWithStringMethod() {
        return "@ModelAttribute注释String返回类型方法,会自动将返回值添加到ModelMap中,键根据返回类型生成.";
    }

    /**
     * @ModelAttribute注释
     *  String类型返回值方法. 
     *  会自动将返回值添加到ModelMap中,键是@ModelAttribute的name或value属性值.
     */
    @ModelAttribute("defModelAttributeName")
    public String modelAttributeWithStringMethodDefName() {
        return "@ModelAttribute注释String类型返回值方法,会自动将返回值添加到ModelMap中,键是@ModelAttribute的name或value属性值.";
    }

    /**
     * ModelAttribute.do.
     */
    @RequestMapping(
            value = "/ModelAttribute.do",
            method = RequestMethod.GET)
    public ModelAndView modelAttribute(ModelMap modelMap) throws Exception {
        logger.info("@ModelAttribute注解测试.");
        return new ModelAndView("webannotations/ModelAttribute", modelMap);
    }
    
    /**
     * ModelAttribute.do.
     * @ModelAttribute与@RequestMapping共同注释同一方法测试.
     */
    @ModelAttribute
    @RequestMapping(
            value = "/ModelAttributeWithRequestMapping.do",
            method = RequestMethod.GET)
    public String modelAttributeWithRequestMapping() throws Exception {
        logger.info("@ModelAttribute与@RequestMapping共同作用在一个方法.");
        return "webannotations/ModelAttribute";
    }
    

    /**
     * ModelAttributeParameters.do.
     * @ModelAttribute注释参数,可以从ModelMap中取指定参数值.
     */
    @RequestMapping(
            value = "/ModelAttributeParameters.do",
            method = RequestMethod.GET)
    public ModelAndView modelAttributeParameters(@ModelAttribute("defModelAttributeName") String modelAttr,
            ModelMap modelMap) throws Exception {
        logger.info("@ModelAttribute注释方法参数,从ModelMap中取值:[key=defModelAttributeName, value=" + modelAttr + "].");
        return new ModelAndView("webannotations/ModelAttribute", modelMap);
    }

}

2) 建ModelAttributeHandlerInterceptor,用来打印演示ModelMap的值。

package com.arhorchin.securitit.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSON;

/**
 * @author Securitit.
 * @note @ModelAttribute查看ModelMap测试.
 */
public class ModelAttributeHandlerInterceptor implements HandlerInterceptor {

    /**
     * logger.
     */
    private Logger logger = LoggerFactory.getLogger(ModelAttributeHandlerInterceptor.class);

    /**
     * 在HandlerAdapter实际调用处理程序之后调用,但在DispatcherServlet呈现视图之前调用.
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            @Nullable ModelAndView modelAndView) throws Exception {
        logger.info("==============================ModelAttributeHandlerInterceptor postHandle==============================\n"
                + JSON.toJSONString(modelAndView.getModelMap(), true));
    }

}

3) 启动服务,访问//localhost:9199/spring-annotations/WebAnnotations/xxxxx.do,用来查看@ModelAttribute各种使用方法。

① 访问//localhost:9199/spring-annotations/WebAnnotations/ModelAttribute.do,演示@ModelAttribute注释单独方法。

控制台输出:

2020-12-16 16:04:26 INFO [c.a.s.i.ModelAttributeHandlerInterceptor] ==============================ModelAttributeHandlerInterceptor postHandle==============================
{
    "defModelAttributeName":"@ModelAttribute注释String类型返回值方法,会自动将返回值添加到ModelMap中,键是@ModelAttribute的name或value属性值.",
    "string":"@ModelAttribute注释String返回类型方法,会自动将返回值添加到ModelMap中,键根据返回类型生成.",
    "modelAttributeWithVoidMethod":"@ModelAttribute注释在void返回类型的方法上."
}

可以看到,@ModelAttribute注释的modelAttributeWithVoidMethod(...)、modelAttributeWithStringMethod()、modelAttributeWithStringMethodDefName(),按照解析的语义已经执行。

② 访问//localhost:9199/spring-annotations/WebAnnotations/ModelAttributeWithRequestMapping.do,演示@ModelAttribute与@RequestMapping共同注释一个方法。

控制台输出:

2020-12-16 16:11:37 INFO [c.a.s.i.ModelAttributeHandlerInterceptor] ==============================ModelAttributeHandlerInterceptor postHandle==============================
{
    "defModelAttributeName":"@ModelAttribute注释String类型返回值方法,会自动将返回值添加到ModelMap中,键是@ModelAttribute的name或value属性值.",
    "modelAttributeWithVoidMethod":"@ModelAttribute注释在void返回类型的方法上.",
    "string":"webannotations/ModelAttribute"
}

浏览器响应:

在这里插入图片描述

除了①中所示语义外,还改变了@RequestMapping注释方法返回值的语义,将返回值按照规则添加到ModelMap中,视图则是由/ModelAttributeWithRequestMapping.do来确定的。

③ 访问//localhost:9199/spring-annotations/WebAnnotations/ModelAttributeParameters.do,演示@ModelAttribute注释方法参数。

2020-12-16 16:31:23 INFO [c.a.s.i.ModelAttributeHandlerInterceptor] ==============================ModelAttributeHandlerInterceptor postHandle==============================
{
    "defModelAttributeName":"@ModelAttribute注释String类型返回值方法,会自动将返回值添加到ModelMap中,键是@ModelAttribute的name或value属性值.",
    "modelAttributeWithVoidMethod":"@ModelAttribute注释在void返回类型的方法上.",
    "string":"@ModelAttribute注释String返回类型方法,会自动将返回值添加到ModelMap中,键根据返回类型生成."
}
2020-12-16 16:31:41 INFO [c.a.s.w.ModelAttributeController] @ModelAttribute注释方法参数,从ModelMap中取值:[key=defModelAttributeName, value=@ModelAttribute注释String类型返回值方法,会自动将返回值添加到ModelMap中,键是@ModelAttribute的name或value属性值.].

可以看到,@ModelAttribute注释方法将从ModelMap中获取值,绑定到方法参数上。

总结

@ModelAttribute主要针对ModelMap进行操作,对于传统的、前后端未分离的应用来说,用处还是很大的。

源码解析基于spring-framework-5.0.5.RELEASE版本源码。

若文中存在错误和不足,欢迎指正!

到此这篇关于Spring中的@ModelAttribute模型属性绑定详解的文章就介绍到这了,更多相关@ModelAttribute模型属性绑定内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • spring5 webclient使用指南详解

    spring5 webclient使用指南详解

    本篇文章主要介绍了spring 5 webclient使用指南详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • java 生成文字图片的示例代码

    java 生成文字图片的示例代码

    本篇文章主要介绍了java 生成文字图片的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • Java8的Stream()与ParallelStream()的区别说明

    Java8的Stream()与ParallelStream()的区别说明

    这篇文章主要介绍了Java8的Stream()与ParallelStream()的区别说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Java中notify是顺序唤醒还是随机唤醒的

    Java中notify是顺序唤醒还是随机唤醒的

    这篇文章主要介绍了Java中notify是顺序唤醒还是随机唤醒的,有很多人会认为 notify 是随机唤醒的,但它真的是随机唤醒的吗?带着疑问一起进入文章了解具体的内容吧
    2022-05-05
  • Java后台线程操作示例【守护线程】

    Java后台线程操作示例【守护线程】

    这篇文章主要介绍了Java后台线程操作,结合实例形式分析了java守护线程相关原理、操作技巧与使用注意事项,需要的朋友可以参考下
    2019-09-09
  • springboot @Controller和@RestController的区别及应用详解

    springboot @Controller和@RestController的区别及应用详解

    这篇文章主要介绍了springboot @Controller和@RestController的区别及应用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Spring如何通过注解引入外部资源(PropertySource Value)

    Spring如何通过注解引入外部资源(PropertySource Value)

    这篇文章主要为大家介绍了Spring通过注解@PropertySource和@Value引入外部资源的方法实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • Java DefaultListableBeanFactory接口超详细介绍

    Java DefaultListableBeanFactory接口超详细介绍

    这篇文章主要介绍了Java DefaultListableBeanFactory接口,DefaultListableBeanFactory是整个bean加载的核心部分,是Spring注册机加载bean的默认实现
    2022-11-11
  • SpringBoot + 微信公众号JSAPI支付功能的实现

    SpringBoot + 微信公众号JSAPI支付功能的实现

    这篇文章主要介绍了SpringBoot + 微信公众号JSAPI支付功能的实现,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • MybatisPlus使用代码生成器遇到的小问题(推荐)

    MybatisPlus使用代码生成器遇到的小问题(推荐)

    这篇文章主要介绍了MybatisPlus使用代码生成器遇到的小问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08

最新评论