需要说明的是,使用配置文件的方式来进行缓存项配置,一般情况能满足使用需求,但是灵活性不是很高,如果我们有很多缓存项的情况下写起来会导致配置文件很长。所以一般情况下你也可以选择使用bean的方式来初始化Cache实例。
下面的演示使用bean的方式来注入:
package com.rickiyang.learn.cache; import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.Caffeine; import org.apache.commons.compress.utils.Lists; import org.springframework.cache.CacheManager; import org.springframework.cache.caffeine.CaffeineCache; import org.springframework.cache.support.SimpleCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /** * @author: rickiyang * @date: 2019/6/15 * @description: */ @Configuration public class CacheConfig { /** * 创建基于Caffeine的Cache Manager * 初始化一些key存入 * @return */ @Bean @Primary public CacheManager caffeineCacheManager() { SimpleCacheManager cacheManager = new SimpleCacheManager(); ArrayList<CaffeineCache> caches = Lists.newArrayList(); List<CacheBean> list = setCacheBean(); for(CacheBean cacheBean : list){ caches.add(new CaffeineCache(cacheBean.getKey(), Caffeine.newBuilder().recordStats() .expireAfterWrite(cacheBean.getTtl(), TimeUnit.SECONDS) .maximumSize(cacheBean.getMaximumSize()) .build())); } cacheManager.setCaches(caches); return cacheManager; } /** * 初始化一些缓存的 key * @return */ private List<CacheBean> setCacheBean(){ List<CacheBean> list = Lists.newArrayList(); CacheBean userCache = new CacheBean(); userCache.setKey("userCache"); userCache.setTtl(60); userCache.setMaximumSize(10000); CacheBean deptCache = new CacheBean(); deptCache.setKey("userCache"); deptCache.setTtl(60); deptCache.setMaximumSize(10000); list.add(userCache); list.add(deptCache); return list; } class CacheBean { private String key; private long ttl; private long maximumSize; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public long getTtl() { return ttl; } public void setTtl(long ttl) { this.ttl = ttl; } public long getMaximumSize() { return maximumSize; } public void setMaximumSize(long maximumSize) { this.maximumSize = maximumSize; } } }
创建了一个SimpleCacheManager作为Cache的管理对象,然后初始化了两个Cache对象,分别存储user,dept类型的缓存。当然构建Cache的参数设置我写的比较简单,你在使用的时候酌情根据需要配置参数。
4. 使用注解来对 cache 增删改查
我们可以使用spring提供的 @Cacheable、@CachePut、@CacheEvict等注解来方便的使用caffeine缓存。
如果使用了多个cahce,比如redis、caffeine等,必须指定某一个CacheManage为@primary,在@Cacheable注解中没指定 cacheManager 则使用标记为primary的那个。
cache方面的注解主要有以下5个:
- @Cacheable 触发缓存入口(这里一般放在创建和获取的方法上,@Cacheable注解会先查询是否已经有缓存,有会使用缓存,没有则会执行方法并缓存)
- @CacheEvict 触发缓存的eviction(用于删除的方法上)
- @CachePut 更新缓存且不影响方法执行(用于修改的方法上,该注解下的方法始终会被执行)
- @Caching 将多个缓存组合在一个方法上(该注解可以允许一个方法同时设置多个注解)
- @CacheConfig 在类级别设置一些缓存相关的共同配置(与其它缓存配合使用)
说一下@Cacheable 和 @CachePut的区别:
@Cacheable:它的注解的方法是否被执行取决于Cacheable中的条件,方法很多时候都可能不被执行。
@CachePut:这个注解不会影响方法的执行,也就是说无论它配置的条件是什么,方法都会被执行,更多的时候是被用到修改上。
简要说一下Cacheable类中各个方法的使用:
public @interface Cacheable { /** * 要使用的cache的名字 */ @AliasFor("cacheNames") String[] value() default {}; /** * 同value(),决定要使用那个/些缓存 */ @AliasFor("value") String[] cacheNames() default {}; /** * 使用SpEL表达式来设定缓存的key,如果不设置默认方法上所有参数都会作为key的一部分 */ String key() default ""; /** * 用来生成key,与key()不可以共用 */ String keyGenerator() default ""; /** * 设定要使用的cacheManager,必须先设置好cacheManager的bean,这是使用该bean的名字 */ String cacheManager() default ""; /** * 使用cacheResolver来设定使用的缓存,用法同cacheManager,但是与cacheManager不可以同时使用 */ String cacheResolver() default ""; /** * 使用SpEL表达式设定出发缓存的条件,在方法执行前生效 */ String condition() default ""; /** * 使用SpEL设置出发缓存的条件,这里是方法执行完生效,所以条件中可以有方法执行后的value */ String unless() default ""; /** * 用于同步的,在缓存失效(过期不存在等各种原因)的时候,如果多个线程同时访问被标注的方法 * 则只允许一个线程通过去执行方法 */ boolean sync() default false; }
基于注解的使用方法:
package com.rickiyang.learn.cache; import com.rickiyang.learn.entity.User; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; /** * @author: rickiyang * @date: 2019/6/15 * @description: 本地cache */ @Service public class UserCacheService { /** * 查找 * 先查缓存,如果查不到,会查数据库并存入缓存 * @param id */ @Cacheable(value = "userCache", key = "#id", sync = true) public void getUser(long id){ //查找数据库 } /** * 更新/保存 * @param user */ @CachePut(value = "userCache", key = "#user.id") public void saveUser(User user){ //todo 保存数据库 } /** * 删除 * @param user */ @CacheEvict(value = "userCache",key = "#user.id") public void delUser(User user){ //todo 保存数据库 } }
如果你不想使用注解的方式去操作缓存,也可以直接使用SimpleCacheManager获取缓存的key进而进行操作。
注意到上面的key使用了spEL 表达式。Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:
名称 | 位置 | 描述 | 示例 |
methodName | root对象 | 当前被调用的方法名 | #root.methodname |
method | root对象 | 当前被调用的方法 | #root.method.name |
target | root对象 | 当前被调用的目标对象实例 | #root.target |
targetClass | root对象 | 当前被调用的目标对象的类 | #root.targetClass |
args | root对象 | 当前被调用的方法的参数列表 | #root.args[0] |
caches | root对象 | 当前方法调用使用的缓存列表 | #root.caches[0].name |
Argument Name | 执行上下文 | 当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数 | #artsian.id |
result | 执行上下文 | 方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false) | #result |
注意:
1.当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。如
@Cacheable(key = "targetClass + methodName +#p0")
2.使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。如:
@Cacheable(value="userCache", key="#id") @Cacheable(value="userCache", key="#p0")
SpEL提供了多种运算符
类型 | 运算符 |
关系 | <,>,<=,>=,==,!=,lt,gt,le,ge,eq,ne |
算术 | +,- ,* ,/,%,^ |
逻辑 | &&,||,!,and,or,not,between,instanceof |
条件 | ?: (ternary),?: (elvis) |
正则表达式 | matches |
其他类型 | ?.,?[…],![…],^[…],$[…] |
转自:rickiyang