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

【Spring 默认是否管理 Request 和 Session Bean 的生命周期?】

要测试 Spring 默认是否管理 RequestSession 作用域的 Bean 的生命周期,可以通过以下步骤实现:

  1. 验证 Spring 是否创建了 Bean:检查 Spring 容器是否成功加载并管理了 RequestSession 作用域的 Bean。
  2. 验证 Bean 的生命周期回调方法是否被调用:通过实现生命周期接口(如 InitializingBeanDisposableBean)或使用注解(如 @PostConstruct@PreDestroy),验证初始化和销毁逻辑是否被调用。
  3. 验证 Bean 的作用域行为
    • 对于 Request 作用域的 Bean,每次 HTTP 请求都会创建一个新的实例。
    • 对于 Session 作用域的 Bean,每个 HTTP Session 都会创建一个新的实例。

以下是一个完整的代码示例,展示如何测试 Spring 默认是否管理 RequestSession 作用域的 Bean 的生命周期。


1. 项目结构

src
├── main
│   ├── java
│   │   └── com.example
│   │       ├── MyRequestScopedBean.java
│   │       ├── MySessionScopedBean.java
│   │       └── AppConfig.java
│   └── resources
└── test
    └── java
        └── com.example
            └── RequestSessionBeanTest.java

2. 示例代码

2.1 MyRequestScopedBean.java

这是一个 Request 作用域的 Bean,定义了生命周期回调方法。

package com.example;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;

@Component
@Scope(WebApplicationContext.SCOPE_REQUEST) // 定义为 Request 作用域
public class MyRequestScopedBean {

    public MyRequestScopedBean() {
        System.out.println("MyRequestScopedBean: Constructor called");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("MyRequestScopedBean: @PostConstruct - Initialization logic");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("MyRequestScopedBean: @PreDestroy - Cleanup logic");
    }

    public void doSomething() {
        System.out.println("MyRequestScopedBean: Doing something...");
    }
}

2.2 MySessionScopedBean.java

这是一个 Session 作用域的 Bean,定义了生命周期回调方法。

package com.example;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;

@Component
@Scope(WebApplicationContext.SCOPE_SESSION) // 定义为 Session 作用域
public class MySessionScopedBean {

    public MySessionScopedBean() {
        System.out.println("MySessionScopedBean: Constructor called");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("MySessionScopedBean: @PostConstruct - Initialization logic");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("MySessionScopedBean: @PreDestroy - Cleanup logic");
    }

    public void doSomething() {
        System.out.println("MySessionScopedBean: Doing something...");
    }
}

2.3 AppConfig.java

这是一个 Spring 配置类,启用组件扫描。

package com.example;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
}

2.4 RequestSessionBeanTest.java

这是测试类,验证 RequestSession 作用域的 Bean 的生命周期。

测试代码
package com.example;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

@SpringBootTest
@AutoConfigureMockMvc
public class RequestSessionBeanTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private MyRequestScopedBean requestScopedBeanProxy; // 注入代理对象

    @Autowired
    private MySessionScopedBean sessionScopedBeanProxy; // 注入代理对象

    @Test
    public void testRequestScopedBean() throws Exception {
        // 模拟 HTTP GET 请求
        mockMvc.perform(org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get("/testRequest"))
               .andExpect(org.springframework.test.web.servlet.result.MockMvcResultMatchers.status().isOk());
    }

    @Test
    public void testSessionScopedBean() {
        // 创建一个模拟的 HTTP 请求
        MockHttpServletRequest request = new MockHttpServletRequest();
        RequestContextHolder.setRequestAttributes(new org.springframework.web.context.request.ServletRequestAttributes(request));

        try {
            // 获取 SessionScopedBean 实例
            MySessionScopedBean sessionBean1 = sessionScopedBeanProxy; // 通过代理获取
            sessionBean1.doSomething();

            // 模拟新的 Session
            request.getSession().invalidate(); // 销毁当前 Session
            MockHttpServletRequest newRequest = new MockHttpServletRequest();
            RequestContextHolder.setRequestAttributes(new org.springframework.web.context.request.ServletRequestAttributes(newRequest));

            MySessionScopedBean sessionBean2 = sessionScopedBeanProxy; // 获取新的 SessionScopedBean 实例
            sessionBean2.doSomething();

        } finally {
            // 清理请求上下文
            RequestContextHolder.resetRequestAttributes();
        }
    }
}

2.5 控制器代码

为了测试 RequestSession 作用域的 Bean,可以创建一个简单的控制器。

MyController.java
package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    private final MyRequestScopedBean requestScopedBean;

    @Autowired
    public MyController(MyRequestScopedBean requestScopedBean) {
        this.requestScopedBean = requestScopedBean;
    }

    @GetMapping("/testRequest")
    public String testRequestScopedBean() {
        System.out.println("Accessing RequestScopedBean in controller");
        requestScopedBean.doSomething();
        return "RequestScopedBean tested successfully!";
    }
}

3. 输出结果

3.1 测试 RequestScopedBean

运行 testRequestScopedBean() 方法后,控制台会输出类似以下内容:

MyRequestScopedBean: Constructor called
MyRequestScopedBean: @PostConstruct - Initialization logic
Accessing RequestScopedBean in controller
MyRequestScopedBean: Doing something...
MyRequestScopedBean: @PreDestroy - Cleanup logic
说明
  • 每次 HTTP 请求都会创建一个新的 RequestScopedBean 实例。
  • 请求结束后,@PreDestroy 方法会被调用,销毁该实例。

3.2 测试 SessionScopedBean

运行 testSessionScopedBean() 方法后,控制台会输出类似以下内容:

MySessionScopedBean: Constructor called
MySessionScopedBean: @PostConstruct - Initialization logic
MySessionScopedBean: Doing something...
MySessionScopedBean: @PreDestroy - Cleanup logic
MySessionScopedBean: Constructor called
MySessionScopedBean: @PostConstruct - Initialization logic
MySessionScopedBean: Doing something...
MySessionScopedBean: @PreDestroy - Cleanup logic
说明
  • 第一次请求时,创建了一个 SessionScopedBean 实例。
  • 销毁当前 Session 后,再次请求时会创建一个新的 SessionScopedBean 实例。

4. 验证 RequestSession 的作用域行为

4.1 验证 RequestScopedBean 的独立实例

在测试代码中,可以通过多次请求验证 RequestScopedBean 是否每次都创建新的实例。

示例
mockMvc.perform(get("/testRequest"));
mockMvc.perform(get("/testRequest"));

控制台会输出两次 MyRequestScopedBean: Constructor called,说明每次请求都会创建一个新的实例。


4.2 验证 SessionScopedBean 的独立实例

在测试代码中,可以通过销毁 Session 后再次请求,验证 SessionScopedBean 是否创建新的实例。

示例
request.getSession().invalidate(); // 销毁当前 Session
MockHttpServletRequest newRequest = new MockHttpServletRequest();
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(newRequest));

控制台会输出两次 MySessionScopedBean: Constructor called,说明每次 Session 都会创建一个新的实例。


5. 注意事项

5.1 RequestScopedBeanSessionScopedBean 的依赖注入

  • 在 Spring 中,RequestScopedBeanSessionScopedBean 是动态代理的,因此不能直接注入其实例。
  • 如果需要手动获取 RequestScopedBeanSessionScopedBean,可以通过 @Autowired 注入一个代理对象,并通过 RequestContextHolder 获取当前请求的 Bean。
示例
@Autowired
private MyRequestScopedBean requestScopedBeanProxy;

@Test
public void testRequestScopedBeanProxy() {
    // 手动获取当前请求的 RequestScopedBean 实例
    RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
    if (attributes instanceof ServletRequestAttributes) {
        ServletRequestAttributes servletAttributes = (ServletRequestAttributes) attributes;
        MyRequestScopedBean actualBean = (MyRequestScopedBean) servletAttributes.getRequest().getAttribute("requestScopedBean");
        actualBean.doSomething();
    }
}

5.2 测试环境的要求

  • RequestScopedBeanSessionScopedBean 是 Web 环境下的作用域,因此测试时需要确保 Spring 的 Web 环境已正确加载。
  • 使用 @SpringBootTest 注解可以加载完整的应用上下文。
  • 如果只测试 Web 层,可以使用 @WebMvcTest 注解。

6. 总结

如何测试 RequestSession 作用域的 Bean 的生命周期?

  1. 验证 Bean 的创建

    • 检查构造方法是否被调用。
    • 检查 @PostConstruct 注解的方法是否被调用。
  2. 验证 Bean 的作用域行为

    • 对于 RequestScopedBean,每次 HTTP 请求都会创建一个新的实例。
    • 对于 SessionScopedBean,每个 HTTP Session 都会创建一个新的实例。
  3. 验证 Bean 的销毁

    • 对于 RequestScopedBean,销毁逻辑会在请求结束后自动调用。
    • 对于 SessionScopedBean,销毁逻辑需要手动实现(因为 Spring 不管理 Session 的生命周期)。

通过上述测试代码和验证方法,可以清楚地了解 Spring 如何管理 RequestSession 作用域的 Bean 的生命周期。

相关文章:

  • OpenCV 基础模块 Python 版
  • MySQL 设置允许远程连接完整指南:安全与效率并重
  • 小白闯AI:Llama模型Lora中文微调实战
  • k8s运维 设置Pod实现JVM内存根据容器内存动态调整
  • 车载以太网网络测试-20【传输层-DOIP协议-3】
  • OpenCV图像处理基础2
  • 内网穿透的应用-如何用Docker本地部署轻量级个人云盘ZFile手机电脑异地远程访问
  • 计算属性set方法没被执行的原因
  • React如何导入md5,把密码password进行md5加密
  • uni-app——数据缓存API
  • 深入解析 Java Stream API:筛选根节点的优雅实现!!!
  • 论文阅读笔记:Denoising Diffusion Probabilistic Models (2)
  • Jenkins Pipeline
  • 详解套接字 Socket 与网络 IO 模型演进
  • Android使用RxHttp进行国密4加密解密
  • 如何在IPhone 16Pro上运行python文件?
  • SQL Server 2014 (x64) 中文版安装与使用指南(附安装包)
  • 深入解析 Java Stream API:筛选子节点的优雅实现!!!
  • 金桔网桥路由版3
  • Excel(进阶篇):powerquery详解、PowerQuery的各种用法,逆透视表格、双行表头如何制作透视表、不规则数据如何制作数据透视表
  • 220名“特朗普币”持有者花1.48亿美元,获邀与特朗普共进晚餐
  • 高适配算力、行业大模型与智能体平台重塑工业城市
  • 牟海松任国家信访局副局长
  • “应急侠”上线,应急管理部正式发布应急科普IP形象
  • 宝妈称宝宝在粽子中吃出带血创可贴,来伊份:已内部排查
  • 高培勇:中国资本市场的发展应将预期因素全面纳入分析和监测体系