应改用Spring的@Conditional体系+自定义配置开关+切点精确限定,因System属性是JVM全局静态变量,无模块边界、无法自动清理、状态不同步,易致兄弟模块被误织入;需通过@ConditionalOnProperty实现模块级开关、包路径限定切点、运行时上下文校验三层隔离。

直接改用 Spring 的 @Conditional 体系 + 自定义配置开关 + 切点精确限定,不要用 System.setProperty() 做切面控制。System 属性是 JVM 全局静态变量,无模块边界、无生命周期感知、无法自动清理,一设全生效,天然导致兄弟模块被误织入。
为什么 System 属性会误伤兄弟模块
所有模块共享同一份 System.getProperties(),模块 A 设置 System.setProperty("aop.enabled", "true") 后,模块 B 的类也会被同一个切面匹配;测试结束后属性不会自动还原,污染后续模块加载;Spring 容器启动时切面已注册,而 System 属性可能在任意时刻修改,状态严重不同步。
用 @ConditionalOnProperty 实现模块级开关
每个模块在自己的 application.yml 中独立声明开关:
- 订单模块:
aop: trace-enabled: true - 营销模块:
aop: trace-enabled: false
切面类加上条件注解,确保只在本模块配置开启时才加载:
@Aspect
@Configuration
@ConditionalOnProperty(name = "aop.trace-enabled", havingValue = "true")
public class TraceAspect { ... }用包路径限定切点范围
禁止使用 execution(* *(..)) 这类宽泛表达式。必须显式限定到本模块包:
- ✅ 正确:
@Pointcut("execution(* com.example.order.service..*.*(..))") - ❌ 错误:
@Pointcut("execution(* com.example..*.*(..))")
这样即使多个模块都启用了同名切面,也只会织入本模块代码,物理隔离执行通道。
运行时加一层上下文校验
在模块入口(如 Filter 或 WebMvcConfigurer)中注入标识,切面中主动校验:
- 入口处写入:
MDC.put("module", "order"); - 切面中判断:
if (!"order".equals(MDC.get("module"))) return proceed();
该方式可兜底拦截因配置漏配或 ClassLoader 混杂导致的越界调用,确保执行通道归属清晰。

















