当前位置: 首页 > ops >正文

Spring Start Here 读书笔记:第9章 Using the Spring web scopes

在第五章中,我们学习了Spring bean的范围(scope)包括Singleton(默认,非并发)和Prototype,以及即时实例化和延迟实例化。

本章将学习另外几个和Web应用相关的范围(web scope):

  • 请求范围——Spring 为每个 HTTP 请求创建一个 Bean 类的实例。该实例仅存在于该特定的 HTTP 请求中。
  • 会话范围——Spring 创建一个实例,并将其保存在服务器内存中,以完成整个 HTTP 会话。Spring 将上下文中的实例与客户端会话关联起来。
  • 应用程序范围——该实例在应用上下文中是唯一的,并且在应用运行时可用。

这些都是Spring框架赋予你的能力。

9.1 在 Spring Web 应用中使用请求作用域

请求范围的 Bean 是由 Spring 管理的对象,框架会为每个 HTTP 请求创建一个新的实例。应用只能将该实例用于创建它的请求。任何新的 HTTP 请求(来自同一客户端或其他客户端)都会创建并使用同一类的不同实例。

请求作用域 bean 的关键方面:

事实后果考虑避免
Spring 会为来自任何客户端的每个 HTTP 请求创建一个新的实例。Spring 在应用执行期间会在内存中创建大量此 Bean 的实例。实例数量通常不是什么大问题,因为这些实例的生命周期很短。HTTP 请求完成后,应用会释放这些实例,然后它们会被垃圾回收。但是,请确保不要实现 Spring 创建实例时需要执行的耗时逻辑(例如从数据库获取数据或执行网络调用)。避免在构造函数或 @PostConstruct 方法中为请求范围的 bean 编写逻辑。
只有一个请求可以使用一个请求作用域 bean 的实例。请求作用域 bean 的实例不易出现多线程相关问题,因为只有一个线程(请求所在的线程)可以访问它们。您可以使用实例的属性来存储请求使用的数据。不要对这些 Bean 的属性使用同步技术。这些技术是多余的,而且只会影响应用的性能。

登录应用的场景非常适合演示请求范围Bean,因为用户的用户名口令只用于登录,后续不会保存。当然,在生产环境中,登录建议用Spring Security实现,作者也有一本书讲这个:Spring Security in Action (Manning, 2020)。

参见示例sq-ch9-ex1。这是一个登录验证程序。
登录页面如下:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Login</title>
</head>
<body><form action="/" method="post">Username: <input type="text" name="username" /><br />Password: <input type="password" name="password" /><br /><button type="submit">Log in</button></form><p th:text="${message}"></p>
</body>
</html>

然后,LoginController是控制类,其中并没有存放用户名和口令敏感信息。LoginProcessor是model类,其中有属性用户名和口令,但他的范围是基于请求的(见@RequestScope注解),因此数据也不会长期保留在内存中。

界面如下:
在这里插入图片描述

9.2 在 Spring Web 应用中使用会话作用域

会话是指HTTP会话。会话范围的 Bean 允许我们存储同一客户端的多个请求共享的数据。

会话作用域 Bean适用的场景包括:

  • 登录 - 在经过身份验证的用户访问应用的不同部分并发送多个请求时,保留其详细信息。
  • 在线购物车 - 用户访问应用中的多个位置,搜索并添加到购物车中的商品。购物车会记住客户端添加的所有商品。

会话范围 bean 的关键方面

事实后果考虑避免
会话范围的 bean 实例会在整个 HTTP 会话期间保留。与 requestscoped bean 相比,它们的寿命更长,而且垃圾收集频率更低。应用会将您存储在会话范围的 bean 中的数据保留更长时间。避免在会话中保存过多数据。这可能会造成性能问题。此外,切勿在会话 Bean 属性中存储敏感信息(例如密码、私钥或任何其他机密信息)。
多个请求可以共享会话范围的 Bean 实例。如果同一客户端发出多个并发请求来更改实例上的数据,则可能会遇到与多线程相关的问题,例如竞争条件。当你知道这种情况可能发生时,你可能需要使用同步技术来避免并发。但是,我通常建议你先看看是否可以避免这种情况,并且只有在无法避免的情况下才将同步作为最后的手段。
会话范围的 Bean 是一种通过将数据保存在服务器端来实现请求间数据共享的方法。您实现的逻辑可能意味着请求彼此依赖。当将状态详细信息保存在一个应用的内存中时,客户端会依赖于该特定的应用实例。在决定使用会话范围的 Bean 实现某些功能之前,请考虑其他替代方案,例如将要共享的数据存储在数据库中,而不是会话中。这样,您就可以使 HTTP 请求彼此独立。

参见示例sq-ch9-ex2,他实现了以下4方面:

  1. 创建一个会话范围的 Bean 来保存已登录用户的详细信息。(model类LoginProcessor,仍带@RequestScope注解;服务类LoggedUserManagementService,带@SessionScope注解)
  2. 创建用户只有登录后才能访问的页面。(验证登录使用控制类LoginController)
  3. 确保用户在未登录的情况下无法访问步骤 1 中创建的页面。(控制类MainController中的逻辑实现)
  4. 身份验证成功后,将用户从登录页面重定向到主页面。

在本例中,会话范围Bean存储的只有用户名,登录成功后会设置此用户名(最初为空),登出时会置空此用户名。根据用户名是否为空,就可以实现以上所有的逻辑。

这是登录成功后的页面:
在这里插入图片描述
点击Log out后,又回到登录页面。

9.3 在 Spring Web 应用中使用应用程序作用域

生产系统应避免使用,而是用数据库持久层来实现。

应用程序作用域与单例的工作方式类似。区别在于,应用程序作用域bean在整个应用中只有一个实例,并且在讨论 Web 作用域(包括应用程序作用域)的生命周期时,我们始终使用 HTTP 请求作为参考点。

应用程序作用域的 Bean与单例 Bean 一样,都存在并发问题。最好为单例 Bean 和应用程序作用域Bean设置不可变的属性。但如果将属性设置为不可变,则可以直接使用单例 Bean。

示例sq-ch9-ex3实现了登录计数(也包括失败的登录)。

和上一个例子相比,增加了一个应用范围的服务类LoginCountService :

@Service
@ApplicationScope
public class LoginCountService {private int count;public void increment() {count++;}public int getCount() {return count;}
}

另外,model类也做了微小的改动,即在登录成功后计数器加1:

public boolean login() {loginCountService.increment();
...
}        

最后,main.html稍微修改以显示登录次数。

界面如下:在这里插入图片描述

总结

  • 除了单例和原型 bean 作用域之外,在 Spring Web 应用中还有三个 bean 作用域,他们只在 Web 应用中才有意义,因此我们称之为 Web 作用域:
    • 请求作用域——Spring 为每个 HTTP 请求创建一个 bean 实例。
    • 会话作用域——Spring 为每个客户端的 HTTP 会话创建一个 bean 实例。来自同一客户端的多个请求可以共享同一个实例。
    • 应用程序作用域——对于该特定 bean,整个应用只有一个实例。来自任何客户端的每个请求都可以访问此实例。
  • Spring 保证请求范围的 Bean 实例只能被一个 HTTP 请求访问。因此,您可以放心使用实例的属性,而不必担心并发相关的问题。此外,您也不必担心它们会填满应用的内存。由于它们的生命周期很短,因此在 HTTP 请求结束后,这些实例就会被垃圾回收。
  • Spring 会为每个 HTTP 请求创建请求范围的 Bean 实例。这种情况很常见。最好不要通过在构造函数或 @PostConstruct 方法中实现逻辑来增加实例创建的难度。
  • Spring 将会话范围的 Bean 实例链接到客户端的 HTTP 会话。这样,会话范围的 Bean 实例可用于在来自同一客户端的多个 HTTP 请求之间共享数据。
  • 即使来自同一个客户端,客户端也可以并发发送 HTTP 请求(例如在同一页面中同时发送多个请求)。如果这些请求更改了会话范围实例中的数据,则可能会陷入竞争条件。您需要避免这种情况,或者让您的代码以支持并发。
  • 避免使用应用程序范围的 Bean 实例。由于所有 Web 应用请求共享应用程序范围的 Bean 实例,任何写入操作通常都需要同步,从而造成瓶颈并严重影响应用的性能。此外,这些 Bean 会在应用内存中驻留的时间与应用本身一样长,因此无法被垃圾回收。更好的方法是将数据直接存储在数据库中。
  • 会话范围的 Bean 和应用程序范围的 Bean 都意味着请求的独立性会降低。我们称应用程序管理请求所需的状态(或者说应用程序是有状态的)。有状态的应用程序意味着不同的架构问题,这些问题最好避免。
http://www.xdnf.cn/news/18419.html

相关文章:

  • Excel表格指定数据读取写入到另一个Excel表中(指定列指定行)
  • CXR-LT 2024:一场关于基于胸部X线的长尾、多标签和零样本疾病分类的MICCAI挑战赛|文献速递-深度学习人工智能医疗图像
  • 前端AI工具——TRAE
  • ExcelUtils实现 设置内容 插入行 复制行列格式
  • Blender模型动画导入到UE5
  • 【python】python进阶——推导式
  • 基于 SkyWalking + Elasticsearch + Grafana 的可落地调用链监控方案
  • 氙灯市场报告:亚太成增长主力,汽车、医疗、科研多领域需求驱动行业发展
  • 数据结构 -- 队列
  • Redis内存碎片深度解析:成因、检测与治理实战指南
  • Day16 二叉树part4
  • JDK21之虚拟线程的深入理解
  • Halcon那些事:什么是动态阈值,如何用dyn_threshold分割图片
  • 腾讯云COS SDK签名有效期设置为10分钟到期会自动刷新
  • Java后端学习路线
  • uniapp googlepay支付 内购项目
  • mysql编程(简单了解)
  • pthon实现bilibili缓存视频音频分离
  • 数据预处理学习笔记
  • 【C++】--函数参数传递:传值与传引用的深度解析
  • 防爆自动气象监测设备:高危环境的 “安全堡垒”
  • SpringBoot中的条件注解
  • 工作后的总结和反思1
  • 如何制定股指期货投机交易策略计划?
  • 数字社会学是干什么的?数字社会学理论与数字社会学家唐兴通讲数字社会学书籍有哪些?AI社会学人工智能社会学理论框架
  • 使用jwt+redis实现单点登录
  • LeetCode 回文链表
  • 力扣1005:k次取反后最大化的数组和
  • Elasticsearch官方文档学习-未完待续
  • 三层交换机