@Cached(name = "contractOrPdInfo",timeUnit = TimeUnit.HOURS,expire = 24)
@CacheRefresh(refresh = 1,timeUnit = TimeUnit.HOURS)
public ContractDetailVO findDetailByPdBaseIdAndContractBaseIdAndVersion(Integer pdBaseId, Integer contractBaseId, Double version) {
code.....
}
比如我使用注解创建了方法缓存,我方法返回的vo是 几张表的数据组合而成的,这几张表的数据有改动的话,这个方法的缓存也会过期.JetCache 提供了CacheRefresh 注解,我们可以设置缓存自动刷新时间,到了时间,他会解析出每个缓存key对应的方法参数,然后调用你这个方法,把得到的结果更新到缓存的value中.但是实时性不高.不满足我的需求.
于是我就想办法如何主动通知jetcache,去清除这个方法的所有缓存,因为方法参数并不包含我那几张表的全部id,所以只能吧缓存全部清除,让他下一次读取不到缓存.
要解决这个问题要有以下几个步骤:
1 给表定义别名 我称之为 namespace 比如t_user的namespace是 user
2 添加自定义注解,获取一个方法的缓存数据依赖哪几张表. 注解名字为:AutoClearCache
/**
* fhs framework 对于 jetcahce的扩展注解
* 用于namespaces 的数据改变之后 自动清除缓存
* by wanglei
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD,ElementType.TYPE,})
public @interface AutoClearCache {
/**
* 需要自动清除缓存的命名空间
* @return
*/
String[] namespaces() default {};
}
3 搞一个管理器,允许业务根据namespace 去清除此namespace相关的 方法缓存.
管理器大概这么几个部分:
A 项目启动后,根据注解找到所有的需要CacheUpdateManager 管理的所有的方法,缓存起来,建立好和namespace的关系
B 当有业务要调用clearcache的时候,根据namespace找到 相关的方法对象.
然后根据方法对象找到缓存对象(不同版本的jetcache 实现方法不同,我用的2.5.16)
根据方法对象找到所有的刷新缓存的task,根据task可以找到缓存的key
然后调用缓存的.remove方法吧对应的key干掉.
**
* 缓存刷新管理器
* 在项目启动完成后获取所有的@AutoRefresCache 标记的方法
*/
@Data
public class CacheUpdateManager implements ApplicationContextAware, ApplicationListener<ApplicationReadyEvent> {
/**
* service的包路径
*/
private String[] packageNames = new String[]{};
@Autowired
private ConfigMap cacheConfigMap;
private ApplicationContext applicationContext;
private GlobalCacheConfig globalCacheConfig;
/**
* key namespace value value namespace 对应的刷新器
*/
private Map<String, List<MethodPoint>> methodPontMap = new HashMap<>();
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 清除某个namespace的所有缓存
*
* @param namespace
*/
public void clearCache(String namespace) {
List<MethodPoint> methodPoints = methodPontMap.get(namespace);
if (methodPoints == null || methodPoints.isEmpty()) {
return;
}
for (MethodPoint methodPoint : methodPoints) {
clearAndRefreshMethodCache(methodPoint);
}
}
/**
* 清除并且自动刷新方法缓存
*
* @param point 方法
*/
public void clearAndRefreshMethodCache(MethodPoint point) {
if (globalCacheConfig == null) {
globalCacheConfig = applicationContext.getBean(GlobalCacheConfig.class);
}
if (globalCacheConfig == null || !globalCacheConfig.isEnableMethodCache()) {
return;
}
String key = CachePointcut.getKey(point.getMethod(), point.getClazz());
CacheInvokeConfig cac = cacheConfigMap.getByMethodInfo(key);
if (cac == null || cac == CacheInvokeConfig.getNoCacheInvokeConfigInstance()) {
return;
}
CacheInvokeContext context = globalCacheConfig.getCacheContext().createCacheInvokeContext(cacheConfigMap);
context.setCacheInvokeConfig(cac);
context.setHiddenPackages(globalCacheConfig.getHiddenPackages());
CacheInvokeConfig cic = context.getCacheInvokeConfig();
CachedAnnoConfig cachedAnnoConfig = cic.getCachedAnnoConfig();
Cache cache = context.getCacheFunction().apply(context, cachedAnnoConfig);
if(cache == null){
return;
}
if (cache instanceof RefreshCache) {
ConcurrentHashMap<Object, RefreshCache.RefreshTask> taskMap = (ConcurrentHashMap<Object, RefreshCache.RefreshTask>) ReflectUtils.getValue(cache, "taskMap");
if (taskMap != null && !taskMap.isEmpty()) {
Collection<RefreshCache.RefreshTask> tasks = taskMap.values();
for (RefreshCache.RefreshTask task : tasks) {
cache.remove(ReflectUtils.getValue(task, "key"));
}
}
}
}
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
//spring容器初始化完成之后,就会自行此方法。
Set<Class<?>> entitySet = ScannerUtils.scan(AutoClearCache.class, packageNames);
// 遍历所有class,获取所有用@autowareYLM注释的字段
if (entitySet != null) {
String[] namespaces = null;
for (Class<?> entity : entitySet) {
//过滤 包含注解并且是public的缓存
List<Method> methods = ReflectUtils.getAllMethod(entity).stream().filter(method -> (method.getModifiers() & 1) == 1)
.filter(method -> method.isAnnotationPresent(AutoClearCache.class)).collect(Collectors.toList());
for (Method method : methods) {
namespaces = method.getAnnotation(AutoClearCache.class).namespaces();
for (String namespace : namespaces) {
MethodPoint tempPoint = MethodPoint.builder().clazz(entity)
.method(method).build();
List<MethodPoint> points = methodPontMap.containsKey(namespace) ? methodPontMap.get(namespace) : new ArrayList<>();
points.add(tempPoint);
methodPontMap.put(namespace, points);
}
}
}
}
}
}
/**
* 方法
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
class MethodPoint {
private Method method;
private Class clazz;
}
开源项目地址:https://gitee.com/fhs-opensource/fhs-framework
fhs framework qq群:976278956