【Spring Cache】七 Spring Cache 流程总结

【Spring Cache】七 Spring Cache 流程总结

前言

至此,我了解了 Spring Cache 相关的核心 API,也了解了基于 @EableCaching 引入 Spring Cache 的流程以及 Spring Cache 的核心实现逻辑

本章节从一段 demo 入手,从上到下再梳理一遍流程做个总结

demo

CacheConfig

@Configuration
@EnableCaching
public class CacheConfig implements CachingConfigurer {

    // 注册 ConcurrentMapCacheManager
    @Override
    public CacheManager cacheManager() {
        ConcurrentMapCacheManager cacheManager
                = new ConcurrentMapCacheManager();
        return cacheManager;
    }

    // 自定义配置 KeyGenerator
    @Override
    public KeyGenerator keyGenerator() {

        return new PrefixSimpleKeyGenerator("configurer");
    }

    // 注册一个 KeyGenerator 的 bean,用来在注解上指定
    @Bean
    public KeyGenerator customKeyGenerator() {

        return new PrefixSimpleKeyGenerator("custom");
    }

    // 稍微拓展一下 SimpleKeyGenerator
    public static class PrefixSimpleKeyGenerator extends SimpleKeyGenerator {

        private String prefix;

        public PrefixSimpleKeyGenerator(String prefix) {
            this.prefix = prefix;
        }

        @Override
        public Object generate(Object target, Method method, Object... params) {
            return prefix + super.generate(target, method, params);
        }
    }
}
  • 缓存的配置类,实现了 CachingConfigurer 并给出 cacheManagerkeyGenerator 的配置,基于直接章节的了解,它们的优先级高于缺省属性,低于缓存注解上指定的属性
  • 为了方便演示,这里使用的是 ConcurrentMapCacheManager,且没有指定固定缓存,具体细节之前的章节有了解过
  • 在容器中单独注册一个 KeyGenerator 用来缓存注解上指定对应属性,以方便缓存测试
  • PrefixSimpleKeyGenerator,拓展了一下 SimpleKeyGenerator,可以添加指定前缀

DemoHandler

@Component
@CacheConfig(cacheNames = "default")
public class DemoHandler {

    // 内存管理一组 User
    static MultiValueMap<String, User> map = new LinkedMultiValueMap();

    static {
        map.add("a", new User(1, "a"));
        map.add("a", new User(2, "a"));
        map.add("b", new User(3, "b"));
    }
    
    @Cacheable(cacheNames = "core")
    public List<User> query(String name) {

        System.out.println("method query invoke ...");
        return map.get(name);
    }

    @CacheEvict(cacheNames = "core", allEntries = true)
    public void add(User user) {

        System.out.println("method add invoke ...");
        map.add(user.getName(), user);
    }

    @CacheEvict(allEntries = true)
    public void add2(User user) {

        System.out.println("method add2 invoke ...");
        map.add(user.getName(), user);
    }

    @CacheEvict(cacheNames = "core", keyGenerator = "customKeyGenerator")
    public void add3(User user) {

        System.out.println("method add3 invoke ...");
        map.add(user.getName(), user);
    }

    public static class User {

        private Integer id;
        private String name;

        // ...
        
    }
}

缓存操作业务类:

  • 基于内存以 name 维度管理了一组 User
  • @CacheConfig 注解可以理解为该类所有缓存方法的全局属性配置,它的优先级低于具体缓存方法操作上的属性,但比 CacheConfigurer 自定义的属性优先级高(此处即缓存方法不指定属性 cacheNames 时,默认 default
  • query 方法是一个 CacheableOperation:根据 name 获取 User 集合并缓存至 core 缓存
  • add 方法是一个 CacheEvictOperation:添加一个用户时清空所有的 core 缓存(其实我们可以指定更加精细的 KeyGenerator 实现指定的缓存更新,示例从简)
  • add2 方法也是一个 CacheEvictOperation,但未指定 cacheNames,因此:添加一个用户会清空所有的 default 缓存(而不是 core 缓存),即之前的缓存不会失效
  • add3 方法也是一个 CacheEvictOperation,但指定了单独的 keyGenerator,因此:添加一个用户会清空缓存 core 对应 keyGenerator 生成 key 的缓存,即之前的缓存不会失效

TestMain

public class TestMain {

    @Test
    public void test() {
        AnnotationConfigApplicationContext context
                = new AnnotationConfigApplicationContext("com.example.springdemoall.cache.demo");
        DemoHandler bean = context.getBean(DemoHandler.class);

        // 第一次查并插入缓存 core
        System.out.println(bean.query("a"));

        System.out.println("---------");

        // 命中缓存 core
        System.out.println(bean.query("a"));

        System.out.println("---------");

        // 清空缓存 core
        bean.add(new DemoHandler.User(4, "a"));

        System.out.println("---------");

        // 再查一次并缓存 core
        System.out.println(bean.query("a"));

        System.out.println("---------");

        // 清空缓存 default
        bean.add2(new DemoHandler.User(5, "a"));

        System.out.println("---------");

        // 命中缓存 core
        System.out.println(bean.query("a"));

        System.out.println("---------");

        // 清除不同的 key
        bean.add3(new DemoHandler.User(6, "a"));

        System.out.println("---------");

        // 命中缓存 core
        System.out.println(bean.query("a"));
    }
}

这就是最终的测试类,我们想要展示的场景是:

  1. 查询 name = a 的数据,此时没有缓存,但 CacheableOperation 会在执行查询后更新缓存 core
  2. 此时再查询 name = a 的数据,命中缓存 core 直接返回不执行目标方法
  3. 调用 add 方法会清空缓存 core 的所有数据(因为 beforeInvocation = true)
  4. 此时再查询 name = a 的数据,无法命中缓存,CacheableOperation 会在执行查询后再次更新缓存 core
  5. 调用 add2 方法会清空缓存 default 的所有数据,则再查询 name = a 的数据,还是会命中缓存 core 直接返回而不执行目标方法
  6. 调用 add3 方法会清空缓存 core 的另一个 key 的数据,则再查询 name = a 的数据,还是会命中缓存

整体流程

  • 配置类上的 @EnableCaching注解引入的对应配置类,注册了 InfrastructureAdvisorAutoProxyCreator,它会收集容器中 role = BeanDefinition.ROLE_INFRASTRUCTUREAdvisor 对指定的 bean 进行代理
  • Spring Cache 提供的 Advisor 就是 BeanFactoryCacheOperationSourceAdvisor,一般的理解:Advisor = Pointcut + Advice
    • 此处的 Pointcut 是基于 AnnotationCacheOperationSourceCacheOperationSourcePointcut,方法和类的匹配以及对应 CacheOperations 的解析都是由 AnnotationCacheOperationSource 实现的
    • 此处的 Advice 就是 CacheInterceptor,因此标注了缓存相关注解的方法在被代理增强后就拥有了 Spring Cache 的能力:
      • @Cacheable 注解的方法支持从缓存中查询结果以及未命中后缓存后设置缓存
      • @CachePut 注解的方法支持缓存的插入(更新)
      • @CacheEvict 注解的方法支持缓存的剔除

Spring Boot

当前这个示例可以理解为 Spring 下的使用,那 Spring Boot 自然是提供了大量现成的装配类以供我们方便的使用各种 Spring Cache 的实现

  • CacheProperties,此类支持我们使用 spring.cache 前缀属性对 Spring Cache 的属性进行指定,比如 spring.cache.type = redis,更多属性甚至到某个实现的细节(比如 redis 缓存的超时设置等)配置可自行参考
  • CacheType,枚举类,定义了 Spring Boot 都支持哪些缓存实现的装配,比如本示例中的缓存实现在 Spring Boot 简化为 spring.cache.type = simple 即可,其他的值还有 redis jcache
  • CacheConfigurations 维护着每种缓存实现类型对应的配置类,想了解更多细节可以自行参考

总结

收尾篇,结合一个示例展示了简单的 Spring Cache 实现场景以及实现流程

上一篇:【Spring Cache】六 CacheInterceptor 相关

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值