Spring Cache 学习总结

Spring Cache 学习总结

    关于 Cache
    SpringBoot Cache
    注解驱动缓存
    redis cache
        redis缓存原理
        redis两级缓存实现

一、关于 Cache
    1.应用系统需要通过 Cache 来缓存不经常改变的数据以提高系统性能和增加系统吞吐量,避免直接访问数据库等低速的存储系统。
    2.Spring Cache 对 Cache 进行了抽象, 提供了@Cacheable、@CachePut、@CacheEvict 等注解。Spring Boot 应用基于 Spring Cache,既提供了基于内存实现的缓存管理器,可以用于单体应用系统,也集成了 Redis、 EhCache 等缓存服务器,可用于大型系统或者分布式系统。
    3.应用系统缓存通常有以下作用:
        缓存 Web 系统的输出,如伪静态页面。 
        缓存系统中不经常改变的业务数据,如用户权限、字典数据、配置信息等。 
    4.cache的组件与概念
        Cache 相关的组件与概念如下:
         · CacheManager,用来创建、管理、管理多个命名唯一的 Cache,如组织机构缓存、菜单项的缓存、菜单树的缓存等。 
         · Cache 类似 Map 那样的 Key-Value 存储结构, Value 部分通常包含了缓存的对象,通过 Key 来取得缓存对象。 
         · 缓存项,存放在缓存里的对象,常常需要实现序列化接口,以支持分布式缓存。 
         · Cache 存储方式,缓存组件可以将对象放到内存或其他缓存服务器上,Spring Boot 提供了一个基于 ConcurrentMap 的缓存,同时也集成了Redis、EhCache 2.x、 JCache 缓存服务器等。
         · 缓存策略,通常 Cache 还可以有不同的缓存策略,如设置缓存最大的容量,缓存项的过期时间等。 
         · 分布式缓存,缓存通常按照缓存数据类型存放在不同缓存服务器上,或者同一类型的缓存按照某种算法、不同 Key 的数据存放在不同的缓存服务器上。 
         · Cache Hit,从 Cache 中取得期望的缓存项,我们通常称之为缓存命中。如果没有命中则称之为 Cache Miss,意味着需要从数据来源处重新取出井放回 Cache 中。 
         · Cache Miss,缓存丢失,根据 Key 没有从缓存中找到对应的缓存项。 
         · Cache Evication,缓存清除操作。 
         · Hot Data,热点数据,缓存系统能调整算法或者内部存储方式,使得最有可能频繁访问的数据能被尽快访问到。 
         · On-Heap, Java 分配对象都是在堆内存中,有最快的获取速度。由于虚拟机的垃圾回收管理机制,缓存放入过多的对象会导致垃圾回收时间过长,从而有可能影响性能。 
         · Off-Heap,堆外内存,对象存放在虚拟机分配的堆外内存中,因此不受垃圾回收管理机制的管理,不影响系统性能,但堆外内存的对象要被使用,还要序列化成堆内对象。 
         · 很多缓存工具会把不常用的对象放到堆外,把热点数据放到堆内。
    5.一级二级缓存服务器
        使用 Redis 缓存,通过网络访问还是不如从内存中获取的性能好,所以通常称为二级缓存,从内存中取得缓存数据称为一级缓存。当应用系统需要查询缓存的时候,先从一级缓存里查找,如果有,则返回,如果没有查找到,则再查询二级缓存。

二、SpringBoot Cache
    1.Spring Boot 本身提供了一个基于 ConcurrentHashMap 的缓存机制,也集成了 EhCache2.x、JCache CJSR-107、EhCache3.x、Hazeleast、Infinispan),还有 Couchbase、Redis 等。 
    2.Spring Boot 应用通过注解的方式使用统一的缓存,只需在方法上使用缓存注解即可,其缓存的具体实现依赖于选择的目标缓存管理器。
    3.集成 Spring Cache 的 pom 依赖:
         
            org.springframework.boot 
            spring-boot-starter-cache 
        
 
        如果使用 Spring 自带的内存的缓存管理器,需要在 appliaction. properties 中配置属性:
        spring.cache.type=Simple 
        Simple,是基于 ConcurrentHashMap 实现的缓存,适合单体应用或者开发环境使用。
        none,关闭缓存,比如开发阶段为了确保功能正确,可以先禁止使用缓存。
        redis,使用 Redis 作为缓存,还需要在 porn 中增加 Redis 依赖。 
        Generic,用户自定义缓存实现,用户需要自定义一个 org.springframework.cache.CacheManager 的实现。
    
三、注解驱动缓存
    配置好 Spring Boot 缓存,就可以在 Spring 管理的 Bean 中使用缓存注解,通常可以直接放在 Service 类上。
    1.@Cacheable,作用在方法上,触发缓存读取操作。
        对于不同的缓存实现,最好将缓存对象实现序列化 Serializable 接口, 这样可以无缝切换到分布式缓存系统,比如:public class Menu implements Serializable
        Spring 使用 SimpleKeyGenerator 来实现上述 Key 的生成:
        public class SimpleKeyGenerator implements KeyGenerator { 
            @Override 
            public Object generate (Object target, Method method, Object ... params) {
                return generateKey(params) ; 
            }
            public static Object generateKey(Object ... params) {
                if (params.length == 0) {
                    return SimpleKey. EMPTY ; 
                }
                if (params.length == 1) {
                    Object param = params[O];
                    if (param ! = null && ! param.getClass().isArray () ) {
                        return param; 
                    }
                return new SimpleKey(params); 
            }
        }
        SimpleKey 实现了 hashCode 和 equals 方法:
        this.hashCode = Arrays.deepHashCode(this.params) ; 
        @Override 
        public boolean equals(Object obj) {
            return (this == obj || (obj instanceof SimpleKey && Arrays.deepEquals (
                this.params, ( (SimpleKey) obj).params))) ; 
            }
        }
        也可以实现自己的 KeyGenerator 方法,比如:
        @Cacheable (cacheNames=”org”, keyGenerator=” myKeyGenerator") 
        public Org findOrg (User user) { ... } 
        myKeyGenerator 实现了 KeyGenerator 接口 , 然后从 user 中获取到 orgld 作为缓存的 Key:
        @Override public Object generate(Object target, Method method, Object ... params) {
            User user= (User)params [O]; return user.getOrgid();
        }

    2.通常情况下,直接使用 SpEL 表达式来指定 Key 比自定义一个 KeyGenerator 更为简单:
        @Cacheable(cacheNames=”org”, key=”#orgid”) 
        public Org findOrg(Long orgid, boolean checkCrmSystem) { ... } 
        @Cacheable(cacheNames=”org”, key=”#user.orgid”) 
        public Org findOrg(User user) { ... } 
        第一个方法仅仅使用参数 orgld 作为 Key,忽略参数 checkCrmSystem, 第二个方法则获取 user 的 orgld 属性作为 Key。
    3.也可以通过条件表达式来指定是否需要缓存,比如 orgld 大于 1000 的都需要缓存:
        @Cacheable(cacheNames=”org”, condition="#orgid>lOOO") 
        public Org findOrg (Long orgid) { ... } 
    4.还可以根据方法返回的结果来决定是否缓存,以下返回值如果 status 为 0,则不缓存:
        @Cacheable(cacheNames=”org”, unless=”#result.status==0”)
        public Org findOrg (Long orgid) { ... }
    5.@CacheEvict,用于删除缓存项或者清空缓存, CacheEvict 可以指定多个缓存名字来清空多个缓存.
    6.@CachePut,作用在方法上,触发缓存更新操作。注解 CachePut 总是会执行方法体,井且使用返回的结果更新缓存。
        @Caching,作用在方法上,综合上面的各种操作,在有些场景下,调用业务会触发多种缓存操作。
        @CacheConfig,在类上设置当前缓存的一些公共设置。注解 CacheConfig 作用于类上,可以为此类的方法的缓存注解提供默认值,包括:缓存的默认名称、缓存的 KeyGenerator;

四、redis cache
    1.定制缓存
        对于 RedisCacheManager 来说,还可以定制缓存项的存活时间,缓存名是否在 Redis 中加上前缀等,这是通过实现 CacheManagerCustomizer 配置来完成定制的:
        @Configuration 
        public class RedisCacheManagerCustomizer {
            @Bean 
            public CacheManagerCustomizer cacheManagerCustomizer() {
                return new CacheManagerCustomizer() {
                    @Override 
                    public void customize(RedisCacheManager cacheManager) {
                        Map expires= new HashMap() ; 
                        //设直 menu 缓存,一分钟过期 
                        expires.put(”menu”, 601); 
                        cacheManager.setExpires(expires) ; 
                    }
                };
            }
        }
    2.redis缓存原理
        |- Spring Boot 的自动配直类 RedisCacheConfiguration 会自动检测 spring.cache.type 的值,从而决定使用何种缓存。
            @Configuration //向容器中添加配置类
            @AutoConfigureAfter(RedisAutoConfiguration.class) 在Redis自动配置成功才会加载redis缓存自动配置
            @ConditionalOnBean(RedisTemplate.class) // 条件:有RedisTemplate类实例
            @ConditionalOnMissingBean(CacheManager.class) 条件:无CacheManager实例对象
            @Conditional(CacheCondition.class) // CacheCondition 实现了 Condition 接口,该接口返回一个 boolean 值,用于判断是否能启用该配置。 CacheCondition 用于判断 application.properties 中是否配置了 spring.cache.type,取出其对应的值与 CacheCondition 所在的配置类进行比较,如果一致,则返回 true(match)。 
            class RedisCacheConfiguration {
                @Bean 
                public RedisCacheManager cacheManager(RedisTemplate redisTemplate) { 
                    RedisCacheManager cacheManager = new RedisCacheManager (redisTemplate); 
                    return cacheManager; 
                }
            }
        |- RedisCacheManager 用于实现 Redis 的缓存管理, RedisCacheManager 实现了 CacheManager 接口,CacheManager 有如下定义:
            public interface CacheManager {
                Cache getCache(String name); 
                Collection getCacheNames(); 
            }
            getCache 方法会根据缓存名字得到缓存,getCacheNames 则返回 CacheManager 知道的缓存。RedisCacheManager 将返回 RedisCache,其实现了Cache 接口,类似Java.util.Map 接口,包含以下方法:
            ValueWrapper get(Object key),根据 Key 值获取缓存对象,ValueWrapper 包含了 get 方法用于获取缓存对象。
            void put(Object key, Object value),设置缓存。
            void evict(Object key),根据 Key 值清除缓存。
            void clear(),清除所有缓存。

    3.redis两级缓存实现    
        |- Spring Boot 自带的 Redis 缓存非常容易使用,但由于通过网络访问了 Redis,效率还是比传统的跟应用部署在一起的一级缓存略慢。 
        |- 常见使用方式:在访问 Redis 之前,先访问一个 ConcurrentHashMap 实现的简单一级缓存,如果有缓存项,则返回给应用,如果没有,再从 Redis 中取得,并将缓存对象放到一级缓存中。
        |- 当缓存项发生变化的时候,注解@CachePut 和@CacheEvict会触发 RedisCache 的 put(Object key, Object Value)和 evict(Object Key)操作,两级缓存需要同时更新 ConcurrentHashMap 和 Redis 缓存,而且需要通过 Redis 的 Pub 发出通知消息,其他 Spring Boot 应用通过 Sub 来接收消息,同步更新 Spring Boot 应用自身的一级缓存。
      代码实现:IDEA快捷键:手动清理无用的import --> Ctrl+alt+o ;设置自动清理Ctrl + Alt + S 打开设置,Editor->General->Auto import->勾选Optimize imports on the fly 
      可参考:Spring Boot 集成 Cache缓存

你可能感兴趣的:(springboot学习)