java用注解实现redis缓存
用注解实现 redis 缓存
@CacheConfig
主要用于配置该类中会用到的一些共用的缓存配置。示例:
@CacheConfig(cacheNames = "users")
public interface UserService {...}
配置了该数据访问对象中返回的内容将存储于名为 users 的缓存对象中,我们也可以不使用该注解,直接通过 @Cacheable 自己配置缓存集的名字来定义。
@Cacheable
可以标记在一个方法上,也可以标记在一个类上,表示该方法 / 类是支持缓存的;Spring 会在其被调用后将其返回值缓存起来。下次利用同样的参数来执行该方法时可以直接从缓存中获取结果。
参数介绍
• value、cacheNames:两个等同的参数(cacheNames 为 Spring 4 新增,作为 value 的别名),用于指定缓存存储的集合名。由于 Spring 4 中新增了 @CacheConfig,因此在 Spring 3 中原本必须有的 value 属性,也成为非必需项了
• key:缓存对象存储在 Map 集合中的 key 值,非必需,缺省按照函数的所有参数组合作为 key 值,若自己配置需使用 SpEL 表达式,比如:@Cacheable(key = "#p0"):使用函数第一个参数作为缓存的 key 值,更多关于 SpEL 表达式的详细内容可参考官方文档
• condition:缓存对象的条件,非必需,也需使用 SpEL 表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = "#p0", condition = "#p0.length() < 3"),表示只有当第一个参数的长度小于 3 的时候才会被缓存。
• unless:另外一个缓存条件参数,非必需,需使用 SpEL 表达式。它不同于 condition 参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对 result 进行判断。
• keyGenerator:用于指定 key 生成器,非必需。若需要指定一个自定义的 key 生成器,我们需要去实现 org.springframework.cache.interceptor.KeyGenerator 接口,并使用该参数来指定。需要注意的是:该参数与 key 是互斥的
• cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用
• cacheResolver:用于指定使用那个缓存解析器,非必需。需通过 org.springframework.cache.interceptor.CacheResolver 接口来实现自己的缓存解析器,并用该参数指定。
示例如下:
@Cacheable(value = "user", key = "#id")
User selectUserById(final Integer id);
• 关于使用 key 属性自定义 key
用来指定 Spring 缓存方法的返回结果时对应的 key, 支持 SpringEL 表达式。没有指定该属性时,Spring 将使用默认策略生成 key。
使用方法参数时我们可以直接使用“# 参数名”或者“#p+ 参数的 index”(参数 index 按照顺序从 0 开始)
例子:
@Cacheable(value="users", key="#id")
public User find(Integer id) {
return null;
}
@Cacheable(value="users", key="#p0")
public User find(Integer id) {
return null;
}
@Cacheable(value="users", key="#user.id")
public User find(User user) {
return null;
}
@Cacheable(value="users", key="#p0.id")
public User find(User user) {
return null;
}
@CachePut
应用到写数据的方法上,如新增 / 修改方法,调用方法时会自动把相应的数据放入缓存,示例如下:
@CachePut(value = "user", key = "#user.id")
public User save(User user) {
users.add(user);
return user;
}
此时会以 user.id 做为缓存 key, 返回结果 user 做为值
@CachePut 的参数与 @Cacheable 类似
@CacheEvict
应用到移除数据的方法上,如删除方法,调用方法时会从缓存中移除相应的数据,示例如下:
@CacheEvict(value = "user", key = "#id")
void delete(final Integer id);
参数介绍
除了同 @Cacheable 一样的参数之外,@CacheEvict 还有下面两个参数:
• allEntries:非必需,默认为 false。当为 true 时,会移除所有数据
• beforeInvocation:非必需,默认为 false,会在调用方法之后移除数据。当为 true 时,会在调用方法之前移除数据。
@解决数据一致性的两种方式
1. 使用 @CachePut
在“查询”方法上添加 @Cacheable(value=”testValue”, key= “#testKey”) 注解,对方法的返回值进行缓存,在“新增 / 修改”方法上添加 @CachePut(value=”testValue”, key= “#testKey”) 注解,当方法调用成功后,会对缓存 testValue 上 key 值为 testKey 的缓存进行更新,更新内容为“新增 / 修改”的返回值,因此“查询”方法与“新增 / 修改”方法的返回值类型应该一致。
E.g.
user 实体包含属性: id / username / userage / usersex
@CachePut(value = "user", key = "#user.id#user.name#user.pass") // 用 id 作为缓存的 key
public User get(User user) {
User user = users.getById(user.id); //id = 123; username = “zhangsan”; userage = 18; usersex = “1”;
return user;
}
@CachePut(value = "user", key = "#user.id") // 方法调用成功后更新 key = 123 的缓存
public User update(User user) {
userService.updateById(user); //id = 123; username = “lisi”;
return user;
}
执行 update 方法后,当再次调用 get(User user) 查询方法时,redis 内 user 缓存上 key = 123 仍然存在且 user 实体会变为 :(id = 123; username = “zhangsan”; userage = ; usersex = “”;)
由于返回值造成了缓存与数据库数据不一致的问题。
2. 使用 @CacheEvict
在“查询”方法上添加 @Cacheable(value=”testValue”, key= “#testKey”) 注解,对方法的返回值进行缓存,在“新增 / 修改”方法上添加 @CacheEvict(value=”testValue”, key= “#testKey”) 注解,当方法调用成功后,会删除缓存 testValue 上 key 值为 testKey 的缓存。
由于缓存中 testValue 上的 key = 123 已经被删除,再次调用“查询”方法时,会直接查库,因此不存在数据不一致的问题。
@Cacheable 注解不生效的问题
1. 内部方法的调用导致 @Cacheable 失效
@Cacheable 是基于 Spring AOP 代理类,内部方法调用是不走代理的,@Cacheable 是不起作用的
2. 缓存的对象必须实现 Serializable
问题
1. 针对 key 的失效时间设置,未实现
2. 分页时的缓存较复杂,未实现