当前位置: 首页 > 工具软件 > FHS-Framework > 使用案例 >

fhs-framework jetcache 缓存维护之自动清除缓存

岑毅庵
2023-12-01
 @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/blob/v2.x/fhs_extends/fhs_cache/src/main/java/com/alicp/jetcache/CacheUpdateManager.java

开源项目地址:https://gitee.com/fhs-opensource/fhs-framework

fhs framework qq群:976278956

 类似资料: