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 依赖:
如果使用 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
return new CacheManagerCustomizer
@Override
public void customize(RedisCacheManager cacheManager) {
Map
//设直 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
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缓存