调试Spring Boot缓存应用:如何查找Cacheable等接口的实现类?
调试Spring Boot缓存注解:找到实现类并验证操作
嘿,我来帮你搞定这个Spring Boot缓存调试的问题!其实@Cacheable、@CacheConfig这些注解的背后,是Spring的AOP代理机制在工作,下面我一步步给你讲怎么找到核心实现类,以及如何验证缓存操作是否正常执行。
一、核心实现类在哪里?
Spring缓存注解的处理逻辑主要集中在这几个关键类里:
CacheInterceptor:这是缓存切面的核心拦截器,所有带有@Cacheable、@CacheEvict、@CachePut注解的方法,都会被这个类拦截,它负责处理缓存的读取、填充、清空等核心操作。SpringCacheAnnotationParser:这个类负责解析@CacheConfig以及方法级的缓存注解,会把@CacheConfig中定义的统一配置(比如缓存名称、key生成器等)应用到类内的方法注解上。- 具体的
Cache实现类:比如默认的ConcurrentMapCache(内存缓存)、RedisCache(Redis缓存),这些类负责实际的缓存存储操作,由对应的CacheManager(比如ConcurrentMapCacheManager、RedisCacheManager)创建和管理。
二、怎么验证缓存操作是否正常?
1. 断点调试直接跟踪
最简单直接的方式就是在IDE里打断点:
- 在
CacheInterceptor的invoke或doInvoke方法上打断点,当调用带有缓存注解的方法时,程序会进入这里,你可以查看当前缓存的键、是否命中缓存,以及后续是否执行了缓存写入/清空操作。 - 如果要验证
@CacheConfig的解析是否生效,可以在SpringCacheAnnotationParser的parseCacheAnnotations方法上断点,查看类级别的配置是否被正确读取并应用到方法上。
2. 自定义缓存监控类
如果不想用断点,也可以自己写一个缓存管理器的包装类,给所有缓存操作加上日志输出:
@Component public class MonitoringCacheManager implements CacheManager { private final CacheManager delegate; // 构造注入原始的缓存管理器 public MonitoringCacheManager(CacheManager delegate) { this.delegate = delegate; } @Override public Cache getCache(String name) { Cache cache = delegate.getCache(name); return cache != null ? new MonitoringCache(cache) : null; } @Override public Collection<String> getCacheNames() { return delegate.getCacheNames(); } // 包装Cache类,添加日志监控 private static class MonitoringCache implements Cache { private final Cache delegate; public MonitoringCache(Cache delegate) { this.delegate = delegate; } @Override public String getName() { return delegate.getName(); } @Override public Object getNativeCache() { return delegate.getNativeCache(); } @Override public ValueWrapper get(Object key) { ValueWrapper result = delegate.get(key); System.out.printf("缓存[%s]读取键[%s],结果:%s%n", getName(), key, result != null ? result.get() : "未命中"); return result; } @Override public <T> T get(Object key, Class<T> type) { T result = delegate.get(key, type); System.out.printf("缓存[%s]读取键[%s],类型[%s],结果:%s%n", getName(), key, type.getName(), result); return result; } @Override public <T> T get(Object key, Callable<T> valueLoader) { try { T result = delegate.get(key, valueLoader); System.out.printf("缓存[%s]加载键[%s],值:%s%n", getName(), key, result); return result; } catch (ValueRetrievalException e) { System.err.printf("缓存[%s]加载键[%s]失败:%s%n", getName(), key, e.getMessage()); throw e; } } @Override public void put(Object key, Object value) { delegate.put(key, value); System.out.printf("缓存[%s]写入键[%s],值:%s%n", getName(), key, value); } @Override public void evict(Object key) { delegate.evict(key); System.out.printf("缓存[%s]移除键[%s]%n", getName(), key); } @Override public void clear() { delegate.clear(); System.out.printf("缓存[%s]已清空所有内容%n", getName()); } } }
启动项目后,所有的缓存操作都会在控制台打印日志,你可以直观看到缓存是否被正确填充、读取或清空。
3. 用Spring Boot Actuator监控
如果你的项目已经引入了Actuator依赖,可以开启缓存端点来监控:
- 首先在
application.properties里配置:
management.endpoints.web.exposure.include=caches management.endpoint.caches.enabled=true
- 启动项目后,访问
/actuator/caches可以查看所有缓存的基本信息;访问/actuator/caches/{cacheName}可以查看具体缓存的详情。 - 还可以发送POST请求到
/actuator/caches/{cacheName}来手动清空缓存,验证清空操作是否生效。
三、额外提示
如果你用的是Redis等第三方缓存,核心的拦截逻辑还是CacheInterceptor,只是实际的存储操作由RedisCache这类实现类完成,你可以在对应的Cache实现类的方法上打断点,跟踪具体的缓存交互过程。
内容的提问来源于stack exchange,提问作者Thiago
相关产品推荐
相关产品推荐

