You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 09:22:01