从原理层面掌握@RequestAttribute、@SessionAttribute的使用【享学Spring MVC】

简介: 从原理层面掌握@RequestAttribute、@SessionAttribute的使用【享学Spring MVC】

前言


如果说知道@SessionAttributes这个注解的人已经很少了,那么不需要统计我就可以确定的说:知道@RequestAttribute注解的更是少之又少。我觉得主要有如下两个原因:


  1. @RequestAttribute这个注解很新,Spring4.3后才有
  2. 我们可以使用API调用的方式(ServletRequest.getAttribute())来达到目的,而不用注解。且成本也不太高


既然Spring推出了这个注解,那必然有它的优点。本文就带大家领略一下它的风骚之处。


Spring提供的这些注解比如@ModelAttribute、@SessionAttributes、@RequestAttribute都是为了简化开发,提高复用性。同时另外一个目的是希望完全屏蔽掉源生Servlet API,增加它的扩展性。


本文我以@RequestAttribute为例进行讲解,因为@SessionAttribute(也是Spring4.3后推出的注解)不管从使用和原理上都是一模一样的。你可以理解成唯一区别是ServletRequest.getAttribute()和HttpSession.getAttribute()的区别

此处再强调一次,这里指的是:org.springframework.web.bind.annotation.SessionAttribute,而非org.springframework.web.bind.annotation.SessionAttributes


@RequestAttribute


它比前面介绍的那些@ModelAttribute、@SessionAttributes等注解要简单很多,它只能使用在方法入参上。作用:从request中取对应的属性值。


很多小伙伴对getParameter()和getAttribute()相关方法傻傻分不清楚。建议你可以先弄清楚param和attribute的区别~

// @since 4.3
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestAttribute {
  @AliasFor("name")
  String value() default "";
  @AliasFor("value")
  String name() default "";
  // 默认情况下 这个属性是必须的(没有就报错了)
  boolean required() default true;
}


接下来这句话很重要:@RequestAttribute只负责从request里面取属性值,至于你什么时候往里放值,是有多种方式的可以达到的:


  1. @ModelAttribute注解预存
  2. HandlerInterceptor拦截器中预存
  3. 请求转发带过来


下面分别按照这三种使用场景,给出使用Demo:


@ModelAttribute注解预存

比较简单,在@ModelAttribute标注的方法上使用源生的HttpServletRequest放值即可


@RestController
@RequestMapping
public class HelloController {
  // 放置attr属性值
    @ModelAttribute
    public Person personModelAttr(HttpServletRequest request) {
        request.setAttribute("myApplicationName", "fsx-application");
        return new Person("非功能方法", 50);
    }
    @GetMapping("/testRequestAttr")
    public void testRequestAttr(@RequestAttribute("myApplicationName") String myApplicationName, HttpServletRequest request, ModelMap modelMap) {
        System.out.println(myApplicationName); //fsx-application
        // 从request里获取
        System.out.println(request.getAttribute("myApplicationName")); //fsx-application
        // 从model里获取
        System.out.println(modelMap.get("myApplicationName")); // null 获取不到attr属性的
        System.out.println(modelMap.get("person")); // Person(name=非功能方法, age=50)
    }
}

请求/testRequestAttr,结果打印如下:


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


这里务必注意:@RequestAttribute("myApplicationName")注解如果省略,是绑定不到attr属性的哦(必须要有注解)~


但是,这样是可行的:@RequestAttribute String myApplicationName(若注解没有指定,Spring MVC会再去看形参的名字来确认自动绑定)

但若你写成了这样@RequestAttribute String aaa,那请求就直接400错误了抛出异常:org.springframework.web.bind.ServletRequestBindingException


HandlerInterceptor拦截器中预存


简单,直接上代码:

public class SimpleInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        request.setAttribute("myApplicationName", "fsx-application");
        return true;
    }
  ...
}


测试代码:略。


forward请求转发带过来


形如这样:

request.setAttribute("myApplicationName", "fsx-application");
request.getRequestDispatcher("/index").forward(request, response); 

其实往里放置属性值只需要遵循一个原则:在调用处理器目标方法之前(参数封装之前)任意地方放置即可,属性值是都能被取到的。

原理剖析


按照我的习惯,即使它很简单,我也会扒开来看看它的原理部分嘛。

根据经验很容易想到解析它的是一个HandlerMethodArgumentResolver,它就是RequestAttributeMethodArgumentResolver


RequestAttributeMethodArgumentResolver


很明显,它也是@since 4.3才出现的,命名上也很配套嘛。

public class RequestAttributeMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
  // 只处理标注了@RequestAttribute注解的入参
  @Override
  public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasParameterAnnotation(RequestAttribute.class);
  }
  // 封装此注解的属性到NamedValueInfo 这里关于参数名的处理有这么一个处理
  // info.name.isEmpty()也就说如果自己没有指定,就用形参名parameter.getParameterName()
  @Override
  protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
    RequestAttribute ann = parameter.getParameterAnnotation(RequestAttribute.class);
    Assert.state(ann != null, "No RequestAttribute annotation");
    return new NamedValueInfo(ann.name(), ann.required(), ValueConstants.DEFAULT_NONE);
  }
  // 从request请求域去找属性值
  @Override
  @Nullable
  protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request){
    return request.getAttribute(name, RequestAttributes.SCOPE_REQUEST);
  }
  // 若值不存在,抛出异常ServletRequestBindingException
  @Override
  protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException {
    throw new ServletRequestBindingException("Missing request attribute '" + name +
        "' of type " +  parameter.getNestedParameterType().getSimpleName());
  }
}


源码短小精悍,非常简单。

其实它解析入参方面的核心解析流程在其父类AbstractNamedValueMethodArgumentResolver身上,但并不是本文的重点,请详见HandlerMethodArgumentResolver的章节~


@RequestAttribute属性required默认为true, request.getAttribute获取不到参数就会抛出异常ServletRequestBindingException;required设置为false,即使没有从request中获取到就忽略跳过,赋值为null;

总结


这篇文章介绍了@RequestAttribute的使用Demo,以及它很简单的原理分析。相较于之前所有文章,这篇是非常轻松的,希望可以提供给大家一个思路,来使用@RequestAttribute提高你的逼格,哈哈(友情提示:装逼需谨慎哦~)


说明:因为@SessionAttribute的使用甚至原理几乎一毛一样,所以不用再重复篇幅了


相关文章
|
8天前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
19 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事务管理的核心原理,并给出相应的源码示例。
128 9
|
1月前
|
前端开发 Java 应用服务中间件
【Spring】Spring MVC的项目准备和连接建立
【Spring】Spring MVC的项目准备和连接建立
57 2
|
2月前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
1月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
123 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原理详解
69 0
下一篇
无影云桌面