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

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

前言


该注解顾名思义,作用是将Model中的属性同步到session会话当中,方便在下一次请求中使用(比如重定向场景~)。

虽然说Session的概念在当下前后端完全分离的场景中已经变得越来越弱化了,但是若为web开发者来说,我仍旧强烈不建议各位扔掉这个知识点,so我自然就建议大家能够熟练使用@SessionAttributes来简化平时的开发,本文带你入坑~


@SessionAttributes


我把这行字放在最前面:本文讲解的是org.springframework.web.bind.annotation.SessionAttributes而非org.springframework.web.bind.annotation.SessionAttribute,它两可完全不是一个概念哦~

关于@SessionAttribute的使用在这里


这个注解只能标注在类上,用于在多个请求之间传递参数,类似于Session的Attribute。

但不完全一样:一般来说@SessionAttributes设置的参数只用于暂时的传递,而不是长期的保存,长期保存的数据还是要放到Session中。(比如重定向之间暂时传值,用这个注解就很方便)


官方解释:当用@SessionAttributes标注的Controller向其模型Model添加属性时,将根据该注解指定的名称/类型检查这些属性,若匹配上了就顺带也会放进Session里。匹配上的将一直放在Sesson中,直到你调用了SessionStatus.setComplete()方法就消失了~~~


// @since 2.5   它只能标注在类上
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SessionAttributes {
  // 只有名称匹配上了的  Model上的属性会向session里放置一份~~~
  @AliasFor("names")
  String[] value() default {};
  @AliasFor("value")
  String[] names() default {};
  // 也可以拿类型来约束
  Class<?>[] types() default {};
}

注意理解这句话:用户可以调用SessionStatus.setComplete来清除,这个方法只是清除@SessionAttributes里的参数,而不会应用于Session中的参数。也就是说使用API自己放进Session内和使用@SessionAttributes注解放进去还是有些许差异的~


Demo Show


下面用一个比较简单的例子演示一下@SessionAttributes它的作用:

@Controller
@RequestMapping("/sessionattr/demo")
@SessionAttributes(value = {"book", "description"}, types = {Double.class})
public class RedirectController {
    @RequestMapping("/index")
    public String index(Model model, HttpSession httpSession) {
        model.addAttribute("book", "天龙八部");
        model.addAttribute("description", "我乔峰是个契丹人");
        model.addAttribute("price", new Double("1000.00"));
        // 通过Sesson API手动放一个进去
        httpSession.setAttribute("hero", "fsx");
        //跳转之前将数据保存到Model中,因为注解@SessionAttributes中有,所以book和description应该都会保存到SessionAttributes里(注意:不是session里)
        return "redirect:get";
    }
    // 关于@ModelAttribute 下文会讲
    @RequestMapping("/get")
    public String get(@ModelAttribute("book") String book, ModelMap model, HttpSession httpSession, SessionStatus sessionStatus) {
        //可以从model中获得book、description和price的参数
        System.out.println(model.get("book") + ";" + model.get("description") + ";" + model.get("price"));
        // 从sesson中也能拿到值
        System.out.println(httpSession.getAttribute("book"));
        System.out.println("API方式手动放进去的:" + httpSession.getAttribute("hero"));
        // 使用@ModelAttribute也能拿到值
        System.out.println(book);
        // 手动清除SessionAttributes
        sessionStatus.setComplete();
        return "redirect:complete";
    }
    @RequestMapping("/complete")
    @ResponseBody
    public String complete(ModelMap modelMap, HttpSession httpSession) {
        //已经被清除,无法获取book的值
        System.out.println(modelMap.get("book"));
        System.out.println("API方式手动放进去的:" + httpSession.getAttribute("hero"));
        return "sessionAttributes";
    }
}


我们只需要访问入口请求/index就可以直接看到控制台输出如下:


天龙八部;我乔峰是个契丹人;1000.0
天龙八部
API方式手动放进去的:fsx
天龙八部
null
API方式手动放进去的:fsx


浏览器如下图:


image.png


初识的小伙伴可以认真的观察本例,它佐证了我上面说的理论知识。


@SessionAttributes注解设置的参数有3类方式去使用它:


  1. 在视图view中(比如jsp页面等)通过request.getAttribute()或session.getAttribute获取
  2. 在后面请求返回的视图view中通过session.getAttribute或者从model中获取(这个也比较常用)
  3. 自动将参数设置到后面请求所对应处理器的Model类型参数或者有@ModelAttribute注释的参数里面(结合@ModelAttribute一起使用应该是我们重点关注的)


通过示例知道了它的基本使用,下面从原理层面去分析它的执行过程,实现真正的掌握它。


SessionAttributesHandler

见名之意,它是@SessionAttributes处理器,也就是解析这个注解的核心。管理通过@SessionAttributes标注了的特定会话属性,存储最终是委托了SessionAttributeStore来实现。


// @since 3.1
public class SessionAttributesHandler {
  private final Set<String> attributeNames = new HashSet<>();
  private final Set<Class<?>> attributeTypes = new HashSet<>();
  // 注意这个重要性:它是注解方式放入session和API方式放入session的关键(它只会记录注解方式放进去的session属性~~)
  private final Set<String> knownAttributeNames = Collections.newSetFromMap(new ConcurrentHashMap<>(4));
  // sessonAttr存储器:它最终存储到的是WebRequest的session域里面去(对httpSession是进行了包装的)
  // 因为有WebRequest的处理,所以达到我们上面看到的效果。complete只会清楚注解放进去的,并不清除API放进去的~~~
  // 它的唯一实现类DefaultSessionAttributeStore实现也简单。(特点:能够制定特殊的前缀,这个有时候还是有用的)
  // 前缀attributeNamePrefix在构造器里传入进来  默认是“”
  private final SessionAttributeStore sessionAttributeStore;
  // 唯一的构造器 handlerType:控制器类型  SessionAttributeStore 是由调用者上层传进来的
  public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
    Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
    this.sessionAttributeStore = sessionAttributeStore;
    // 父类上、接口上、注解上的注解标注了这个注解都算
    SessionAttributes ann = AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
    if (ann != null) {
      Collections.addAll(this.attributeNames, ann.names());
      Collections.addAll(this.attributeTypes, ann.types());
    }
    this.knownAttributeNames.addAll(this.attributeNames);
  }
  // 既没有指定Name 也没有指定type  这个注解标上了也没啥用
  public boolean hasSessionAttributes() {
    return (!this.attributeNames.isEmpty() || !this.attributeTypes.isEmpty());
  }
  // 看看指定的attributeName或者type是否在包含里面
  // 请注意:name和type都是或者的关系,只要有一个符合条件就成
  public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) {
    Assert.notNull(attributeName, "Attribute name must not be null");
    if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) {
      this.knownAttributeNames.add(attributeName);
      return true;
    } else {
      return false;
    }
  }
  // 把attributes属性们存储起来  进到WebRequest 里
  public void storeAttributes(WebRequest request, Map<String, ?> attributes) {
    attributes.forEach((name, value) -> {
      if (value != null && isHandlerSessionAttribute(name, value.getClass())) {
        this.sessionAttributeStore.storeAttribute(request, name, value);
      }
    });
  }
  // 检索所有的属性们  用的是knownAttributeNames哦~~~~
  // 也就是说手动API放进Session的 此处不会被检索出来的
  public Map<String, Object> retrieveAttributes(WebRequest request) {
    Map<String, Object> attributes = new HashMap<>();
    for (String name : this.knownAttributeNames) {
      Object value = this.sessionAttributeStore.retrieveAttribute(request, name);
      if (value != null) {
        attributes.put(name, value);
      }
    }
    return attributes;
  }
  // 同样的 只会清除knownAttributeNames
  public void cleanupAttributes(WebRequest request) {
    for (String attributeName : this.knownAttributeNames) {
      this.sessionAttributeStore.cleanupAttribute(request, attributeName);
    }
  }
  // 对底层sessionAttributeStore的一个传递调用~~~~~
  // 毕竟可以拼比一下sessionAttributeStore的实现~~~~
  @Nullable
  Object retrieveAttribute(WebRequest request, String attributeName) {
    return this.sessionAttributeStore.retrieveAttribute(request, attributeName);
  }
}


这个类是对SessionAttribute这些属性的核心处理能力:包括了所谓的增删改查。因为要进一步理解到它的原理,所以要说到它的处理入口,那就要来到ModelFactory了~

相关文章
|
19天前
|
SQL Java 数据库连接
对Spring、SpringMVC、MyBatis框架的介绍与解释
Spring 框架提供了全面的基础设施支持,Spring MVC 专注于 Web 层的开发,而 MyBatis 则是一个高效的持久层框架。这三个框架结合使用,可以显著提升 Java 企业级应用的开发效率和质量。通过理解它们的核心特性和使用方法,开发者可以更好地构建和维护复杂的应用程序。
109 29
|
2月前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
153 14
|
2月前
|
设计模式 前端开发 Java
步步深入SpringMvc DispatcherServlet源码掌握springmvc全流程原理
通过对 `DispatcherServlet`源码的深入剖析,我们了解了SpringMVC请求处理的全流程。`DispatcherServlet`作为前端控制器,负责请求的接收和分发,处理器映射和适配负责将请求分派到具体的处理器方法,视图解析器负责生成和渲染视图。理解这些核心组件及其交互原理,有助于开发者更好地使用和扩展SpringMVC框架。
67 4
|
3月前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
234 2
|
3月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
3月前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
57 0
|
4月前
|
JSON 前端开发 Java
SSM:SpringMVC
本文介绍了SpringMVC的依赖配置、请求参数处理、注解开发、JSON处理、拦截器、文件上传下载以及相关注意事项。首先,需要在`pom.xml`中添加必要的依赖,包括Servlet、JSTL、Spring Web MVC等。接着,在`web.xml`中配置DispatcherServlet,并设置Spring MVC的相关配置,如组件扫描、默认Servlet处理器等。然后,通过`@RequestMapping`等注解处理请求参数,使用`@ResponseBody`返回JSON数据。此外,还介绍了如何创建和配置拦截器、文件上传下载的功能,并强调了JSP文件的放置位置,避免404错误。
|
4月前
|
Java Spring 容器
Spring底层原理大致脉络
Spring底层原理大致脉络
|
4月前
|
XML 前端开发 Java
拼多多1面:聊聊Spring MVC的工作原理!
本文详细剖析了Spring MVC的工作原理,涵盖其架构、工作流程及核心组件。Spring MVC采用MVC设计模式,通过DispatcherServlet、HandlerMapping、Controller和ViewResolver等组件高效处理Web请求。文章还探讨了DispatcherServlet的初始化和请求处理流程,以及HandlerMapping和Controller的角色。通过理解这些核心概念,开发者能更好地构建可维护、可扩展的Web应用。适合面试准备和技术深挖
71 0
|
4月前
|
负载均衡 Java API
Spring Cloud原理详解
Spring Cloud原理详解
103 0