SpringAOP 切面类添加@RefreshScope导致逻辑执行两遍原因
@RefreshScope 注解的核心功能是实现配置的自动刷新生效。当配置中心(例如 Consul)内的配置信息出现变动时,凡是被标记了 @RefreshScope 注解的 Bean 都会经历重新初始化的过程,从而能够顺利获取到最新的配置内容。
不过在最近的开发中,发现问题:若在切面(Aspect)类上使用 @RefreshScope 注解,会观察到接口请求被切面进行了两次处理,不符合正常预期。正常情况下,切面逻辑应当只在请求进入和离开指定切点时各执行一次。
可能导致这种异常情况的原因:
-
代理对象的复杂特性:Spring AOP 是借助动态代理机制来实现切面逻辑的织入。对于那些有状态的 Bean(比如带有 @RefreshScope 注解的 Bean),Spring 在处理请求时可能会创建多个不同的代理实例。在某些特定的场景和条件下,这种情况可能会致使切面逻辑被多次执行。
-
Bean 的重新初始化问题:一旦切面类被标记了 @RefreshScope 注解,那么每当配置发生变化并触发上下文刷新操作时,该切面 Bean 就会被重新创建。如果切面逻辑中存在静态变量,或者对其他非 @RefreshScope 的 Bean 存在依赖且处理方式不当,就很可能导致切面逻辑出现异常执行的状况,从表象上看就是切面被执行了两次。
-
Spring Cloud 与 Consul 的集成机制影响:Consul 客户端在监听配置变化的过程中,可能会触发 Spring Context 的部分甚至全部刷新操作,进而导致切面的初始化过程或者执行逻辑出现异常。尤其是在配置更新与 Bean 生命周期管理的交互环节中,这种异常情况更容易发生。
针对上述问题,可以采取以下解决办法:
-
移除切面类上的 @RefreshScope 注解:由于切面通常属于无状态的组件,它们并不需要对配置的动态更新做出响应。因此,最直接且简单的解决方案就是直接将切面类上的 @RefreshScope 注解移除。
-
确保切面逻辑的正确性:认真细致地检查切面逻辑,全面排查是否存在由于 Bean 的重复初始化或者其他逻辑漏洞,而导致切面方法被多次调用的情况。
-
优化配置刷新策略:如果在切面中确实有必要使用某些可配置项,并且希望维持配置的动态更新能力,那么最佳实践是将获取配置的相关代码放置在单独的 property 对象中,避免与其他代码产生混淆和干扰。
综上所述,不建议在切面类上直接使用 @RefreshScope 注解,因为这可能会在运行时引发各种意料之外的问题。确实有动态刷新需要,那配置参数放置在单独的 property 对象,该 property 类上添加 @RefreshScope 注解,在切面中通过依赖注入该 property 配置类。