Spring Boot新增数据只读模式方案选型及@Aspect切面测试相关问题
Spring Boot 只读状态拦截方案相关问题解答
一、无业务代码侵入前提下,切面是否为最优方案
是符合需求的最优方案,核心优势如下:
- 完全无业务侵入:不需要修改现有增删改业务代码,只要切点规则定义准确,即可全局生效
- 逻辑统一管控:所有只读校验规则收拢在切面中,后续调整拦截范围、权限规则、状态读取逻辑时,仅需修改切面代码,无需散改业务实现
- 场景适配性强:既可以拦截HTTP层面的写请求,也可以拦截内部调用(定时任务、MQ消费、RPC调用)触发的写操作,比Spring MVC拦截器、过滤器的覆盖范围更全面
小优化建议:切点优先切Service层的写操作,可以通过方法命名规则(匹配*save*/*update*/*delete*)或自定义@WriteOperation注解标记写方法,比切Controller层的请求注解更精准,避免误拦截。
二、该@Aspect的集成测试编写方式
核心思路是启动Spring上下文,模拟只读/正常两种状态,从请求入口发起调用验证全链路逻辑,示例代码结构如下:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class ReadOnlyAspectIntegrationTest { // 注入测试用HTTP客户端,也可以替换为MockMvc @Autowired private TestRestTemplate testRestTemplate; // Mock状态读取服务,避免依赖真实数据库状态 @MockBean private AppStatusService appStatusService; // 测试只读状态下增删改操作被拦截 @Test public void blockWriteOperationWhenAppIsReadOnly() { // 模拟应用处于只读状态 when(appStatusService.getCurrentStatus()).thenReturn(AppStatus.READ_ONLY); // 发起新增请求 ResponseEntity<CommonResult<Void>> response = testRestTemplate.postForEntity( "/api/user", new UserAddDTO("test", "123456"), new ParameterizedTypeReference<CommonResult<Void>>() {} ); // 验证返回预期错误 assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); assertThat(response.getBody().getMsg()).contains("应用处于只读状态,禁止修改操作"); } // 测试正常状态下增删改操作正常执行 @Test public void allowWriteOperationWhenAppIsNormal() { when(appStatusService.getCurrentStatus()).thenReturn(AppStatus.NORMAL); ResponseEntity<CommonResult<Void>> response = testRestTemplate.postForEntity( "/api/user", new UserAddDTO("test", "123456"), new ParameterizedTypeReference<CommonResult<Void>>() {} ); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); } // 测试只读状态下读操作正常放行 @Test public void allowReadOperationWhenAppIsReadOnly() { when(appStatusService.getCurrentStatus()).thenReturn(AppStatus.READ_ONLY); ResponseEntity<CommonResult<UserVO>> response = testRestTemplate.getForEntity( "/api/user/1", new ParameterizedTypeReference<CommonResult<UserVO>>() {} ); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); } }
三、这类@SpringBootTest切面测试的最佳实践
- 按需裁剪测试上下文:如果不需要启动全量应用,可以通过
@SpringBootTest(classes = {ReadOnlyAspect.class, AppStatusService.class, UserController.class})仅加载测试需要的Bean,大幅提升测试启动速度 - 覆盖全边界场景:必须覆盖「只读状态写操作拦截、只读状态读操作放行、正常状态所有操作放行、特殊角色豁免(如果有该需求)」四类场景
- 避免依赖外部资源:所有外部依赖(状态读取服务、数据库)都用
@MockBean模拟,确保测试用例不会因为外部环境变化而执行失败 - 全链路验证:优先从Controller层发起请求测试,不要直接调用Service层方法,这样可以同时验证切面拦截、
@ControllerAdvice异常处理的全链路逻辑是否符合预期 - 单独校验切点正确性:新增测试用例验证切点不会误拦截非预期方法,比如内部工具方法、未标记为写操作的业务方法,避免后续新增业务代码被误拦截
内容的提问来源于stack exchange,提问作者Sunderi
相关产品推荐
相关产品推荐

