Spring:如何在@ControllerAdvice注解的类中使用注入的Service?
问题原因
- 核心原因是
@RefreshScope与@ControllerAdvice的加载优先级、代理逻辑存在冲突:@RefreshScope是Spring Cloud提供的动态刷新作用域,被其标记的Bean会被封装为CGLIB代理对象,默认懒加载,只有第一次调用时才会完成实例化和依赖注入。而@ControllerAdvice作为全局异常处理器,会在Spring MVC上下文启动的极早阶段被扫描注册,此时@RefreshScope代理还未完成初始化,构造器注入的依赖没有被赋值,就会出现null的情况。 - 次要原因是
@RequiredArgsConstructor与CGLIB代理的兼容问题:
CGLIB生成代理类时要求目标类有可访问的无参构造方法,而@RequiredArgsConstructor只会生成包含final字段参数的构造方法,没有无参构造,导致代理实例化失败,最终字段为空。 - 小概率原因是上下文隔离问题:如果你的项目存在父子上下文分层,
SomeService注册在Spring MVC子上下文,而@ControllerAdvice被父上下文扫描,也会出现注入失败,但该情况下SomeService在其他父上下文的Bean中也无法调用,和你的描述不符,可以优先排除。
解决方案
方案1(最推荐):移除@ControllerAdvice类上的@RefreshScope
绝大多数场景下全局异常处理器不需要动态刷新配置,直接移除@RefreshScope注解即可解决初始化顺序问题,修改后代码如下:
@ControllerAdvice @Slf4j @RequiredArgsConstructor public class ExceptionHandlerController { private final SomeService someService; // 其余逻辑不变 }
如果你的业务需要动态刷新配置,将@RefreshScope移到对应的配置类或者SomeService类上即可,不需要加在异常处理器本身。
方案2:保留@RefreshScope,改用懒加载注入
如果确实需要给异常处理器开启动态刷新,将构造器注入改为带@Lazy注解的字段注入,延迟依赖的加载时机,等代理初始化完成后再注入:
@ControllerAdvice @Slf4j @RefreshScope public class ExceptionHandlerController { @Autowired @Lazy private SomeService someService; // 其余逻辑不变 }
方案3:补充无参构造方法兼容CGLIB代理
要兼容@RequiredArgsConstructor和CGLIB代理,手动补充一个protected权限的无参构造方法给代理实例化使用:
@ControllerAdvice @Slf4j @RequiredArgsConstructor @RefreshScope public class ExceptionHandlerController { private final SomeService someService; // 仅供CGLIB生成代理使用 protected ExceptionHandlerController() { this.someService = null; } // 其余逻辑不变 }
内容的提问来源于stack exchange,提问作者Simon
相关产品推荐
相关产品推荐

