使用@Cacheable/@CacheEvict时Spring Bean自动注入失败问题排查
问题原因
你的问题根源在于**RestClientWorker类中所有方法都被声明为final**,这直接导致Spring无法生成有效的AOP代理来处理缓存和调度注解:
Spring的@Cacheable、@CacheEvict以及@Scheduled注解都依赖AOP代理实现增强逻辑:
- 当Bean没有实现任何接口时,Spring默认使用CGLIB代理,它通过生成目标类的子类,覆盖目标方法来插入缓存/调度的增强代码。
- 但
final方法无法被子类覆盖,CGLIB无法为这类方法生成代理增强逻辑,进而导致代理对象无法正确继承原始Bean的依赖注入状态,最终出现所有成员变量(restClient、urlsConfig等)为null的情况。
解决方案
你可以通过以下两种方式解决:
方案1:移除方法的final修饰符
直接去掉RestClientWorker中所有方法的final关键字,让CGLIB能够正常生成代理子类并覆盖方法,示例:
@Component @Slf4j public class RestClientWorker { private final UrlsConfig urlsConfig; private final RestClient restClient; private final StaticUserCredentials creds; public RestClientWorker(UrlsConfig urlsConfig, RestClient restClient, StaticUserCredentials creds) { this.urlsConfig = urlsConfig; this.restClient = restClient; this.creds = creds; } public ResponseEntity<CatalogOrdersResp> catalogOrders(OrdersReq req) { return restClient.post() .uri(urlsConfig.getOrdersPath()) .body(req) .retrieve() .toEntity(CatalogOrdersResp.class); } @Cacheable("a") public ResponseEntity<CatalogClientContextResp> catalogA() { return restClient.get() .uri(urlsConfig.getCatalogAPath()) .retrieve() .toEntity(CatalogClientContextResp.class); } @Cacheable("b") public ResponseEntity<CatalogClientContextResp> catalogB() { return restClient.get() .uri(urlsConfig.getCatalogBPath()) .retrieve() .toEntity(CatalogClientContextResp.class); } @CacheEvict(value = {"a", "b"}, allEntries = true) @Scheduled(cron = "${cron.scheduling}") public void evictCaches() { log.info("A and B caches evicted"); } }
方案2:让类实现接口
定义一个包含所有公共方法的接口,让RestClientWorker实现该接口,这样Spring可以使用JDK动态代理(基于接口),不受final方法的限制:
public interface RestClientWorkerInterface { ResponseEntity<CatalogOrdersResp> catalogOrders(OrdersReq req); ResponseEntity<CatalogClientContextResp> catalogA(); ResponseEntity<CatalogClientContextResp> catalogB(); void evictCaches(); } @Component @Slf4j public class RestClientWorker implements RestClientWorkerInterface { // 原有代码保持不变,包括final方法 }
额外说明
你的CacheCustomizer和BaseConfig中都配置了缓存名称,这部分是重复的,可以保留其中一种配置方式即可,避免不必要的冲突。
内容的提问来源于stack exchange,提问作者AlexZhur
相关产品推荐
相关产品推荐

