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模型属性绑定内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

最新评论