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

@CacheEvict无法清除Hazelcast集群缓存,求排查问题原因

问题分析与修复:Hazelcast @CacheEvict 无法清除缓存

核心问题总结

当前代码存在三个关键问题,直接导致@CacheEvict注解完全失效:


1. Spring Cache AOP 内部调用不生效

removeFromCache方法直接调用了同一类中的evictFromCache方法,而Spring缓存注解(包括@CacheEvict)基于动态代理实现,同一类内部的方法调用不会触发AOP代理逻辑,因此@CacheEvict注解根本没有被执行。

2. 未配置Spring Cache与Hazelcast的集成桥接

虽然创建了HazelcastInstance客户端,但没有配置HazelcastCacheManager作为Spring Cache的实现载体。启用@EnableCaching后,Spring会默认使用简单内存缓存,和你的Hazelcast集群完全无关,所以@CacheEvict操作的是独立的缓存空间,自然无法清除Hazelcast内的数据。

3. HazelcastClient类存在变量名错误

HazelcastClient组件中注入的变量是myHazelcastInstance,但getCacheMap()方法里调用的是未定义的authHazelcast.getMap("blah-data")——这会导致空指针异常,说明你实际运行的代码存在笔误,也会影响缓存实例的正常获取。


修复步骤

步骤1:修复HazelcastClient的变量名错误

@Component
@Slf4j
public class HazelcastClient {

    @Autowired
    private HazelcastInstance myHazelcastInstance;

    public IMap<String, String> getCacheMap() {
        // 修正变量名,与注入的实例保持一致
        return myHazelcastInstance.getMap("blah-data");
    }

}

步骤2:配置HazelcastCacheManager,关联Spring Cache与Hazelcast集群

在CacheConfiguration类中添加CacheManager的Bean定义:

@Configuration
@RequiredArgsConstructor
@Slf4j
@EnableAsync
@EnableCaching
public class CacheConfiguration {
    @Bean
    public ClientConfig clientConfig() throws IOException {
        ClientConfig clientConfig = new XmlClientConfigBuilder("<the XML file specified above>").build();
        return clientConfig;
    }

    @Bean(name = "myHazelcastInstance")
    public HazelcastInstance hazelcastInstance(ClientConfig clientConfig) {
        return HazelcastClient.newHazelcastClient(clientConfig);
    }

    // 新增:配置Spring Cache使用Hazelcast实现的CacheManager
    @Bean
    public CacheManager cacheManager(HazelcastInstance myHazelcastInstance) {
        return new HazelcastCacheManager(myHazelcastInstance);
    }

}

步骤3:解决内部调用不触发AOP的问题

有两种可选方案:

方案A:抽离缓存操作到独立组件(推荐)

创建专门的缓存操作组件,避免同一类内部调用:

@Component
@Slf4j
public class CacheOperationService {

    @CacheEvict(value = "blah-data", key = "#p0")
    public void evictFromCache(final String key) {
        log.warn("Evicted key: {} from cache", key);
    }
}

在MyServiceImpl中注入该组件,替换内部调用:

@RequiredArgsConstructor
@Service
@Slf4j
public class MyServiceImpl {

    private final HazelcastClient hazelcastClient;
    private final CacheOperationService cacheOperationService;

    // ... 其余方法保持不变 ...

    @Override
    public void removeFromCache(final String key) {
        retrieveMap().remove(key);
        // 调用外部组件方法,触发AOP代理执行@CacheEvict
        cacheOperationService.evictFromCache(key);
        log.debug("Removed key: {} from cache", key);
    }

    // 删除原类中的evictFromCache方法
}

方案B:通过ApplicationContext获取自身代理对象(不推荐,耦合度高)

如果不想抽离组件,可注入ApplicationContext获取自身代理对象:

@RequiredArgsConstructor
@Service
@Slf4j
public class MyServiceImpl implements ApplicationContextAware {

    private final HazelcastClient hazelcastClient;
    private MyServiceImpl selfProxy;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // 获取自身的代理对象
        selfProxy = applicationContext.getBean(MyServiceImpl.class);
    }

    // ... 其余方法保持不变 ...

    @Override
    public void removeFromCache(final String key) {
        retrieveMap().remove(key);
        // 通过代理对象调用,触发AOP执行@CacheEvict
        selfProxy.evictFromCache(key);
        log.debug("Removed key: {} from cache", key);
    }

    @CacheEvict(value = "blah-data", key = "#p0")
    public void evictFromCache(final String key) {
        log.warn("Evicted key: {} from cache", key);
    }
}

步骤4:(可选)统一缓存操作方式

建议要么全程使用Spring Cache注解(@Cacheable、@CacheEvict等),要么全程使用Hazelcast IMap手动操作,避免混合使用导致逻辑混乱。例如用Spring Cache改写添加和获取方法:

@Service
@Slf4j
public class MyServiceImpl {

    private final CacheOperationService cacheOperationService;

    public MyServiceImpl(CacheOperationService cacheOperationService) {
        this.cacheOperationService = cacheOperationService;
    }

    @CachePut(value = "blah-data", key = "#key")
    public String addToCache(final String key, final String value) {
        log.debug("Added key: {} value: {} to cache", key, value);
        return value;
    }

    @Cacheable(value = "blah-data", key = "#key")
    public Object getFromCache(final String key) {
        throw new RuntimeException("No value found for key: " + key);
    }

    public void removeFromCache(final String key) {
        cacheOperationService.evictFromCache(key);
        log.debug("Removed key: {} from cache", key);
    }
}

内容的提问来源于stack exchange,提问作者Space Cadet

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 02:36:03