Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发;
Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache ,ConcurrentMapCache等;
每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
使用Spring缓存抽象时我们需要关注以下两点;
1、确定方法需要被缓存以及他们的缓存策略
2、从缓存中读取之前缓存存储的数据
名称 | 解释 |
---|---|
Cache | 缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等 |
CacheManager | 缓存管理器,管理各种缓存(cache)组件 |
@Cacheable | 主要针对方法配置,能够根据方法的请求参数对其进行缓存 |
@CacheEvict | 清空缓存 |
@CachePut | 保证方法被调用,又希望结果被缓存。 与@Cacheable区别在于是否每次都调用方法,常用于更新 |
@EnableCaching | 开启基于注解的缓存 |
keyGenerator | 缓存数据时key生成策略 |
serialize | 缓存数据时value序列化策略 |
@CacheConfig | 统一配置本类的缓存注解的属性 |
@Cacheable/@CachePut/@CacheEvict 主要的参数
名称 | 解释 |
---|---|
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如: @Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”} |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写, 如果不指定,则缺省按照方法的所有参数进行组合 例如: @Cacheable(value=”testcache”,key=”#id”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false, 只有为 true 才进行缓存/清除缓存 例如:@Cacheable(value=”testcache”,condition=”#userName.length()>2”) |
unless | 否定缓存。当条件结果为TRUE时,就不会缓存。 @Cacheable(value=”testcache”,unless=”#userName.length()>2”) |
allEntries (@CacheEvict ) |
是否清空所有缓存内容,缺省为 false,如果指定为 true, 则方法调用后将立即清空所有缓存 例如: @CachEvict(value=”testcache”,allEntries=true) |
beforeInvocation (@CacheEvict) |
是否在方法执行前就清空,缺省为 false,如果指定为 true, 则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法 执行抛出异常,则不会清空缓存 例如: @CachEvict(value=”testcache”,beforeInvocation=true) |
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="users", key="#id")
@Cacheable(value="users", key="#p0")
SpEL提供了多种运算符
类型 | 运算符 |
---|---|
关系 | <,>,<=,>=,==,!=,lt,gt,le,ge,eq,ne |
算术 | +,- ,* ,/,%,^ |
逻辑 | &&,||,!,and,or,not,between,instanceof |
条件 | ?: (ternary),?: (elvis) |
正则表达式 | matches |
其他类型 | ?.,?[…],![…],^[…],$[…] |
1,项目结构
2,pom以及源码
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.7.RELEASE
com.g7go
spring-boot-starter-cache-demo
0.0.1-SNAPSHOT
spring-boot-starter-cache-demo
缓存
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-cache
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-starter-test
test
org.projectlombok
lombok
1.16.6
org.springframework.boot
spring-boot-maven-plugin
User.class
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private String id;
private String name;
private String address;
private String tel;
private Integer age;
}
UserService.class
public interface UserService {
/**
* 获取用户
*/
User findUser();
/**
* 更新用户信息
*/
void updateUser();
/**
* 清除缓存的用户信息
*/
void clearUser();
}
UserServiceImpl.class
@Service
@CacheConfig(cacheNames = "CacheConfigName")
public class UserServiceImpl implements UserService {
/**
* Cacheable[] cacheable() default {}; //声明多个@Cacheable
* CachePut[] put() default {}; //声明多个@CachePut
* CacheEvict[] evict() default {}; //声明多个@CacheEvict
* 插入用户
*/
@Caching(
put = {
@CachePut(value = "valueName", key = "#user.id"),
@CachePut(value = "valueName", key = "#user.name"),
@CachePut(value = "valueName", key = "#user.address")
}
)
@Override
public User saveUser(User user) {
System.out.println("插入用户...");
return user;
}
/**
* --@Cacheable注解会先查询是否已经有缓存,有会使用缓存,没有则会执行方法并缓存
* 命名空间:@Cacheable的value会替换@CacheConfig的cacheNames(两者必须有一个)
* --key是[命名空间]::[@Cacheable的key或者KeyGenerator生成的key](@Cacheable的key优先级高,KeyGenerator不配置走默认KeyGenerator SimpleKey [])
*/
@Override
@Cacheable(value = {"valueName", "valueName2"}, key = "'keyName1'")
public User findUser() {
System.out.println("执行方法...");
return new User("id1", "张三", "沧州", "123456", 22);
}
/**
* --@CachePut注解的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,
* 和 @Cacheable 不同的是,它每次都会触发真实方法的调用
* 简单来说就是用户更新缓存数据。但需要注意的是该注解的value 和 key 必须与要更新的缓存相同,也就是与@Cacheable 相同
* 默认先执行数据库更新再执行缓存更新
* 注意返回值必须是要修改后的数据
*/
@Override
@CachePut(value = "valueName", key = "'keyName1'")
public User updateUser() {
System.out.println("更新用户...");
return new User("id1", "李四", "北京", "123456", 22);
}
/**
* --@CachEvict 的作用 主要针对方法配置,能够根据一定的条件对缓存进行清空
* 触发缓存清除
* 默认先执行数据库删除再执行缓存删除
*/
@Override
@CacheEvict(value = "valueName", allEntries = true)
public void clearUser() {
System.out.println("清除缓存...");
}
}
CacheConfig.class
@Configuration
public class CacheConfig extends CachingConfigurerSupport {
@Resource
private RedisConnectionFactory factory;
/**
* 自定义生成redis-key
*/
@Override
public KeyGenerator keyGenerator() {
return (o, method, objects) -> {
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName()).append(".");
sb.append(method.getName()).append(".");
for (Object obj : objects) {
sb.append(obj.toString());
}
return sb.toString();
};
}
@Bean
public RedisTemplate
SpringBootStarterCacheDemoApplicationTests.class
@EnableCaching
@SpringBootTest
@RunWith(SpringRunner.class)
public class SpringBootStarterCacheDemoApplicationTests {
@Resource
private UserService userService;
@Test
public void saveUserTest() {
userService.saveUser(new User("id1", "赵六", "衡水", "123456", 22));
}
@Test
public void findUserTest() {
for (int i = 0; i < 3; i++) {
System.out.println("第" + i + "次");
User user = userService.findUser();
System.out.println(user);
}
}
@Test
public void updateUserTest() {
userService.updateUser();
User user = userService.findUser();
System.out.println(user);
}
@Test
public void clearUserTest() {
userService.clearUser();
User user = userService.findUser();
System.out.println(user);
}
}
略