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.分页时的缓存较复杂,未实现