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

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

再看下面的变种例子(重要):


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


访问:/testModelAttr?name=wo&age=10。报错了:


 org.springframework.web.HttpSessionRequiredException: Expected session attribute 'person'
  at org.springframework.web.method.annotation.ModelFactory.initModel(ModelFactory.java:117)
  at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:869)


这个错误请务必重视:这是前面我特别强调的一个使用误区,当你在@SessionAttributes@ModelAttribute一起使用的时候,最容易犯的一个错误。


错误原因代码如下:


  public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod) throws Exception {
    Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
    container.mergeAttributes(sessionAttributes);
    invokeModelAttributeMethods(request, container);
    // 合并完sesson的属性,并且执行完成@ModelAttribute的方法后,会继续去检测
    // findSessionAttributeArguments:标注有@ModelAttribute的入参  并且isHandlerSessionAttribute()是SessionAttributts能够处理的类型的话
    // 那就必须给与赋值~~~~  注意是必须
    for (String name : findSessionAttributeArguments(handlerMethod)) {
      // 如果model里不存在这个属性(那就去sessionAttr里面找)
      // 这就是所谓的其实@ModelAttribute它是会深入到session里面去找的哦~~~不仅仅是request里
      if (!container.containsAttribute(name)) {
        Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
        // 倘若session里都没有找到,那就报错喽
        // 注意:它并不会自己创建出一个新对象出来,然后自己填值,这就是区别。
        // 至于Spring为什么这么设计 我觉得是值得思考一下子的
        if (value == null) {
          throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
        }
        container.addAttribute(name, value);
      }
    }
  }


注意,这里是initModel()的时候就报错了哟,还没到resolveArgument()呢。Spring这样设计的意图???我大胆猜测一下:控制器上标注了@SessionAttributes注解,如果你入参上还使用了@ModelAttribute,那么你肯定是希望得到绑定的,若找不到肯定是你的程序失误有问题,所以给你抛出异常,显示的告诉你要去排错。


修改如下,本控制器上加上这个方法:

    @ModelAttribute
    public Person personModelAttr() {
        return new Person("非功能方法", 50);
    }

(请注意观察下面的几次访问以及对应的打印结果)

访问:/testModelAttr


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


再访问:/testModelAttr

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


访问:/testModelAttr?name=wo&age=10

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


注意:此时model和session里面的值都变了哦,变成了最新的的请求链接上的参数值(并且每次都会使用请求参数的值)。


访问:/testModelAttr?age=11111

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

可以看到是可以完成局部属性修改的

再次访问:/testModelAttr(无请求参数,相当于只执行非功能方法)

Person(name=fsx, age=18)
Person(name=fsx, age=18)

可以看到这个时候model和session里的值已经不能再被非功能方法上的@ModelAttribute所改变了,这是一个重要的结论。

它的根本原理在这里:


  public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod) throws Exception {
    ...
    invokeModelAttributeMethods(request, container);
    ...
  }
  private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
    while (!this.modelMethods.isEmpty()) {
      ...
      // 若model里已经存在此key 直接continue了
      if (container.containsAttribute(ann.name())) {
        ...
        continue;
      }
      // 执行方法
      Object returnValue = modelMethod.invokeForRequest(request, container);
      // 注意:这里只判断了不为void,因此即使你的returnValue=null也是会进来的
      if (!modelMethod.isVoid()){
        ...
        // 也是只有属性不存在 才会生效哦~~~~
        if (!container.containsAttribute(returnValueName)) {
          container.addAttribute(returnValueName, returnValue);
        }
      }
    }
  }


因此最终对于@ModelAttribute和@SessionAttributes共同的使用的时候务必要注意的结论:已经添加进session的数据,在没用使用SessionStatus清除过之前,@ModelAttribute标注的非功能方法的返回值并不会被再次更新进session内


所以@ModelAttribute标注的非功能方法有点初始值的意思哈~,当然你可以手动SessionStatus清楚后它又会生效了


总结


任何技术最终都会落到使用上,本文主要是介绍了@ModelAttribute各种使用case的示例,同时也指出了它和@SessionAttributes一起使用的坑。

@ModelAttribute这个注解相对来说还是使用较为频繁,并且功能强大,也是最近讲的最为重要的一个注解,因此花的篇幅较多,希望对小伙伴们的实际工作中带来帮助,带来代码之美~

相关文章
|
11天前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
23 0
|
1月前
|
Java Spring 容器
Spring底层原理大致脉络
Spring底层原理大致脉络
|
1月前
|
JSON 前端开发 Java
SSM:SpringMVC
本文介绍了SpringMVC的依赖配置、请求参数处理、注解开发、JSON处理、拦截器、文件上传下载以及相关注意事项。首先,需要在`pom.xml`中添加必要的依赖,包括Servlet、JSTL、Spring Web MVC等。接着,在`web.xml`中配置DispatcherServlet,并设置Spring MVC的相关配置,如组件扫描、默认Servlet处理器等。然后,通过`@RequestMapping`等注解处理请求参数,使用`@ResponseBody`返回JSON数据。此外,还介绍了如何创建和配置拦截器、文件上传下载的功能,并强调了JSP文件的放置位置,避免404错误。
|
1月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
134 9
|
1月前
|
前端开发 Java 应用服务中间件
【Spring】Spring MVC的项目准备和连接建立
【Spring】Spring MVC的项目准备和连接建立
57 2
|
1月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
134 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
|
2月前
|
XML 缓存 前端开发
springMVC02,restful风格,请求转发和重定向
文章介绍了RESTful风格的基本概念和特点,并展示了如何使用SpringMVC实现RESTful风格的请求处理。同时,文章还讨论了SpringMVC中的请求转发和重定向的实现方式,并通过具体代码示例进行了说明。
springMVC02,restful风格,请求转发和重定向
|
1月前
|
XML 前端开发 Java
拼多多1面:聊聊Spring MVC的工作原理!
本文详细剖析了Spring MVC的工作原理,涵盖其架构、工作流程及核心组件。Spring MVC采用MVC设计模式,通过DispatcherServlet、HandlerMapping、Controller和ViewResolver等组件高效处理Web请求。文章还探讨了DispatcherServlet的初始化和请求处理流程,以及HandlerMapping和Controller的角色。通过理解这些核心概念,开发者能更好地构建可维护、可扩展的Web应用。适合面试准备和技术深挖
43 0
|
1月前
|
负载均衡 Java API
Spring Cloud原理详解
Spring Cloud原理详解
73 0
|
1月前
|
负载均衡 Java 网络架构
Spring Cloud原理详解
介绍了Spring Cloud的原理和核心组件,包括服务注册与发现、配置管理、负载均衡、断路器、智能路由、分布式消息传递、分布式追踪和服务熔断等,旨在帮助开发人员快速构建和管理微服务架构中的分布式系统。
58 0
下一篇
无影云桌面