从原理层面掌握@ModelAttribute的使用(使用篇)【享学Spring MVC】(上)

简介: 从原理层面掌握@ModelAttribute的使用(使用篇)【享学Spring MVC】(上)

前言


上篇文章 描绘了@ModelAttribute的核心原理,这篇聚焦在场景使用上,演示@ModelAttribute在不同场景下的使用,以及注意事项(当然有些关联的原理也会涉及)。


为了进行Demo演示,首先得再次明确一下@ModelAttribute的作用。


@ModelAttribute的作用


虽然说你可能已经看过了核心原理篇,但还是可能会缺乏一些上层概念的总结。下面我以我的理解,总结一下 @ModelAttribute这个注解的作用,主要分为如下三个方面:


  1. 绑定请求参数到命令对象(入参对象):放在控制器方法的入参上时,用于将多个请求参数绑定到一个命令对象,从而简化绑定流程,而且自动暴露为模型数据用于视图页面展示时使用;
  2. 暴露表单引用对象为模型数据:放在处理器的一般方法(非功能处理方法,也就是没有@RequestMapping标注的方法)上时,是为表单准备要展示的表单引用数据对象:如注册时需要选择的所在城市等静态信息。它在执行功能处理方法(@RequestMapping 注解的方法)之前,自动添加到模型对象中,用于视图页面展示时使用;
  3. 暴露@RequestMapping方法返回值为模型数据:放在功能处理方法的返回值上时,是暴露功能处理方法的返回值为模型数据,用于视图页面展示时使用。


下面针对这些使用场景,分别给出Demo用例,供以大家在实际使用中参考。


@ConstructorProperties讲解

因为在原理篇里讲过,自动创建模型对象的时候不仅仅可以使用空的构造函数,还可以使用java.beans.ConstructorProperties这个注解,因此有必须先把它介绍一波:


官方解释:构造函数上的注释,显示该构造函数的参数如何对应于构造对象的getter方法。


// @since 1.6
@Documented 
@Target(CONSTRUCTOR)  // 只能使用在构造器上
@Retention(RUNTIME)
public @interface ConstructorProperties {
    String[] value();
}


如下例子:

@Getter
@Setter
public class Person {
    private String name;
    private Integer age;
  // 标注注解
    @ConstructorProperties({"name", "age"})
    public Person(String myName, Integer myAge) {
        this.name = myName;
        this.age = myAge;
    }
}


这里注解上的name、age的意思是对应着Person这个JavaBean的getName()和getAge()方法。

它表示:构造器的第一个参数可以用getName()检索,第二个参数可以用getAge()检索,由于方法/构造器的形参名在运行期就是不可见了,所以使用该注解可以达到这个效果。


此注解它的意义何在???

其实说实话,在现在去xml,完全注解驱动的时代它的意义已经不大了。它使用得比较多的场景是之前像使用xml配置Bean这样:


<bean id="person" class="com.fsx.bean.Person">
    <constructor-arg name="name" value="fsx"/>
    <constructor-arg name="age" value="18"/>
</bean>


这样<constructor-arg>就不需要按照自然顺序参数index(不灵活且容易出错有木有)来了,可以按照属性名来对应,灵活了很多。本来xml配置基本不用了,但恰好在@ModelAttribute解析这块让它又换发的新生,具体例子下面会给出的~


java.beans中还提供了一个注解java.beans.Transient(1.7以后提供的):指定该属性或字段不是永久的。 它用于注释实体类,映射超类或可嵌入类的属性或字段。(可以标注在属性上和get方法上)


Demo Show


标注在非功能方法上


@Getter
@Setter
@ToString
public class Person {
    private String name;
    private Integer age;
    public Person() {
    }
    public Person(String myName, int myAge) {
        this.name = myName;
        this.age = myAge;
    }
}
@RestController
@RequestMapping
public class HelloController {
    @ModelAttribute("myPersonAttr")
    public Person personModelAttr() {
        return new Person("非功能方法", 50);
    }
    @GetMapping("/testModelAttr")
    public void testModelAttr(Person person, ModelMap modelMap) {
        //System.out.println(modelMap.get("person")); // 若上面注解没有指定value值,就是类名首字母小写
        System.out.println(modelMap.get("myPersonAttr"));
    }
}


访问:/testModelAttr?name=wo&age=10。打印输出:

Person(name=wo, age=10)
Person(name=非功能方法, age=50)


可以看到入参的Person对象即使没有标注@ModelAttribute也是能够正常被封装进值的(并且还放进了ModelMap里)。


因为没有注解也会使用空构造创建一个Person对象,再使用ServletRequestDataBinder.bind(ServletRequest request)完成数据绑定(当然还可以@Valid校验)


有如下细节需要注意:

1、Person即使没有空构造,借助@ConstructorProperties也能完成自动封装


  // Person只有如下一个构造函数
    @ConstructorProperties({"name", "age"})
    public Person(String myName, int myAge) {
        this.name = myName;
        this.age = myAge;
    }


打印的结果完全同上。


2、即使上面@ConstructorProperties的name写成了myName,结果依旧正常封装。因为只要没有校验bindingResult == null的时候,仍旧还会执行ServletRequestDataBinder.bind(ServletRequest request)再封装一次的。除非加了@Valid校验,那就只会使用@ConstructorProperties封装一次,不会二次bind了~(因为Spring认为你已经@Valid过了,那就不要在凑进去了)


3、即使上面构造器上没有标注@ConstructorProperties注解,也依旧是没有问题的。原因:BeanUtils.instantiateClass(ctor, args)创建对象时最多args是[null,null]呗,也不会报错嘛(so需要注意:如果你是入参是基本类型int那就报错啦~~)


4、虽然说@ModelAttribute写不写效果一样。但是若写成这样@ModelAttribute("myPersonAttr") Person person,也就是指定为上面一样的value值,那打印的就是下面:


Person(name=wo, age=10)
Person(name=wo, age=10)


至于原因,就不用再解释了(参考原理篇)。

另外还需要知道的是:@ModelAttribute标注在本方法上只会对本控制器有效。但若你使用在@ControllerAdvice组件上,它将是全局的。(当然可以指定basePackages来限制它的作用范围~)


标注在功能方法(返回值)上


形如这样:


    @GetMapping("/testModelAttr")
    public @ModelAttribute Person testModelAttr(Person person, ModelMap modelMap) {
      ...
    }


这块不用给具体的示例,因为比较简单:把方法的返回值放入模型中。(注意void、null这些返回值是不会放进去的~)


标注在方法的入参上


该使用方式应该是我们使用得最多的方式了,虽然原理复杂,但对使用者来说还是很简单的,略。


和@RequestAttribute/@SessionAttribute一起使用


参照博文:从原理层面掌握@RequestAttribute、@SessionAttribute的使用【享学Spring MVC】。它俩合作使用是很顺畅的,一般不会有什么问题,也没有什么主意事项


和@SessionAttributes一起使用


@ModelAttribute它本质上来说:允许我们在调用目标方法前操纵模型数据。@SessionAttributes它允许把Model数据(符合条件的)同步一份到Session里,方便多个请求之间传递数值。

下面通过一个使用案例来感受一把:

@RestController
@RequestMapping
@SessionAttributes(names = {"name", "age"}, types = Person.class)
public class HelloController {
    @ModelAttribute
    public Person personModelAttr() {
        return new Person("非功能方法", 50);
    }
    @GetMapping("/testModelAttr")
    public void testModelAttr(HttpSession httpSession, ModelMap modelMap) {
        System.out.println(modelMap.get("person"));
        System.out.println(httpSession.getAttribute("person"));
    }
}


为了看到@SessionAttributes的效果,我这里直接使用浏览器连续访问两次(同一个session)看效果:


第一次访问打印:

Person(name=非功能方法, age=50)
null


第二次访问打印:

Person(name=非功能方法, age=50)
Person(name=非功能方法, age=50)


可以看到@ModelAttribute结合@SessionAttributes就生效了。至于具体原因,可以移步这里辅助理解:从原理层面掌握@ModelAttribute的使用(核心原理篇)【享学Spring MVC】



相关文章
|
25天前
|
Java 关系型数据库 数据库
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
|
2月前
|
前端开发 Java API
Spring Cloud Gateway Server Web MVC报错“Unsupported transfer encoding: chunked”解决
本文解析了Spring Cloud Gateway中出现“Unsupported transfer encoding: chunked”错误的原因,指出该问题源于Feign依赖的HTTP客户端与服务端的`chunked`传输编码不兼容,并提供了具体的解决方案。通过规范Feign客户端接口的返回类型,可有效避免该异常,提升系统兼容性与稳定性。
177 0
|
2月前
|
缓存 安全 Java
Spring 框架核心原理与实践解析
本文详解 Spring 框架核心知识,包括 IOC(容器管理对象)与 DI(容器注入依赖),以及通过注解(如 @Service、@Autowired)声明 Bean 和注入依赖的方式。阐述了 Bean 的线程安全(默认单例可能有安全问题,需业务避免共享状态或设为 prototype)、作用域(@Scope 注解,常用 singleton、prototype 等)及完整生命周期(实例化、依赖注入、初始化、销毁等步骤)。 解析了循环依赖的解决机制(三级缓存)、AOP 的概念(公共逻辑抽为切面)、底层动态代理(JDK 与 Cglib 的区别)及项目应用(如日志记录)。介绍了事务的实现(基于 AOP
111 0
|
2月前
|
SQL Java 数据库连接
Spring、SpringMVC 与 MyBatis 核心知识点解析
我梳理的这些内容,涵盖了 Spring、SpringMVC 和 MyBatis 的核心知识点。 在 Spring 中,我了解到 IOC 是控制反转,把对象控制权交容器;DI 是依赖注入,有三种实现方式。Bean 有五种作用域,单例 bean 的线程安全问题及自动装配方式也清晰了。事务基于数据库和 AOP,有失效场景和七种传播行为。AOP 是面向切面编程,动态代理有 JDK 和 CGLIB 两种。 SpringMVC 的 11 步执行流程我烂熟于心,还有那些常用注解的用法。 MyBatis 里,#{} 和 ${} 的区别很关键,获取主键、处理字段与属性名不匹配的方法也掌握了。多表查询、动态
110 0
|
2月前
|
JSON 前端开发 Java
第05课:Spring Boot中的MVC支持
第05课:Spring Boot中的MVC支持
148 0
|
2月前
|
监控 架构师 NoSQL
spring 状态机 的使用 + 原理 + 源码学习 (图解+秒懂+史上最全)
spring 状态机 的使用 + 原理 + 源码学习 (图解+秒懂+史上最全)
|
4月前
|
前端开发 Java 数据库连接
Spring核心原理剖析与解说
每个部分都是将一种巨大并且复杂的技术理念传达为更易于使用的接口,而这就是Spring的价值所在,它能让你专注于开发你的应用,而不必从头开始设计每一部分。
165 32
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
371 0
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
160 0
|
存储 开发框架 前端开发
[回馈]ASP.NET Core MVC开发实战之商城系统(五)
经过一段时间的准备,新的一期【ASP.NET Core MVC开发实战之商城系统】已经开始,在之前的文章中,讲解了商城系统的整体功能设计,页面布局设计,环境搭建,系统配置,及首页【商品类型,banner条,友情链接,降价促销,新品爆款】,商品列表页面,商品详情等功能的开发,今天继续讲解购物车功能开发,仅供学习分享使用,如有不足之处,还请指正。
255 0