缓存重要性及缓存开发的几个阶段,见,本文主要关注如何优雅的做缓存开发:
-
基于注解的缓存开发,对应用代码无侵入性;
-
可以方的切换第三方缓存框架,无需修改代码;
-
服务调用方(缓存使用者)无需识别是缓存调用还是一般服务调用,举例说明:调用方在调用UserService.getUser(String userId)服务时,无需关心服务具体实现是基于缓存还是直接查询数据库,由服务提供者根据系统实现吞吐量来决定,系统初期压力不大,可以不使用缓存,待到有性能瓶颈了,修改服务实现支持缓存。
Spring cahe可以很好的满足以上需求,还能带来意外的小惊喜。
Spring cache简介
Spring cache基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 Redis),而是一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,即能够达到缓存方法的返回对象的效果。
Spring cache的缓存技术还具备相当的灵活性,不仅能够使用 SpEL(Spring Expression Language)来定义缓存的 key 和各种 condition,还提供开箱即用的缓存临时存储方案,也支持和主流的专业缓存例如 Redis集成。
其特点总结如下:
-
通过少量的配置 annotation 注释即可使得既有代码支持缓存
-
支持开箱即用 Out-Of-The-Box,即不用安装和部署额外第三方组件即可使用缓存
-
支持 Spring Express Language,能使用对象的任何属性或者方法来定义缓存的 key 和 condition
-
支持 AspectJ,并通过其实现任何方法的缓存支持
-
支持自定义 key 和自定义缓存管理者,具有相当的灵活性和扩展性
干货就要先看示例
1.配置一个基于内存的缓存
2.定义一个用户User实体类
3.定义UserService接口和UserServiceImpl,这个实现类是重点。在getUser方法上添加了@Cacheable注解,意思即是如果调用该方法,则先到名为userCache的缓存中根据id找User是否存在,如果不存在才会到数据库查询,并且将查询结果放到缓存中。
-
名为userCache的缓存实现是org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean,在第1步中已经注册;
-
在调用getUser方法时会先到缓存中查找;
-
getUser方法执行完成后会将结果缓存起来,以便下次从缓存中查询;
4.测试
当第一次执行时,因为缓存中没有数据,会查询数据库。第二次再次执行时,因为缓存中已经有数据了,则直接从缓存中获取结果。
Spring cache开发
缓存实现
Spring cache是对缓存的抽象,并不负责具体缓存的实现。所以开发第一步即是配置缓存管理器和具体的缓存实现
-
缓存管理器使用缺省的org.springframework.cache.support.SimpleCacheManager即可,无需改变;
-
缓存实现,在实现的应用中可能会用到多种缓存实现,如基于内存(map)、Redis的,不同的项目使用的缓存实现也可能不同,如下配置了一种名为userCache的内存缓存实现。
@Cacheable
在方法调用之前执行,即先查询缓存,如果缓存中没有数据再执行方法并将结果缓存起来。常用于查询类服务
属性 | 说明 | 示例 |
---|---|---|
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个,如果是多个,则将结果写入多个缓存 | @Cacheable(value={”cache1”,”cache2”} |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 | @CacheCacheable(value="userCache", key="#user.id" ) |
condition | 满足缓存条件的数据才会放入缓存,condition在调用方法之前和之后都会判断 | @CacheCacheable(value="userCache",condition="#user.id.length()>4" ) |
unless | 满足条件不更新缓存,不像condition,该表达只在方法执行之后判断,此时可以拿到返回值result进行判断了 | @CacheCacheable(value="userCache",condition="#user.id.length()>4" )//满足条件不更新缓存 |
@CachePut
在方法调用之后执行,即先执行方法,再缓存结果。常用于更新类服务。
注:缓存key是user对象的id, 缓存结果是方法的返回值,无论是CachePut还是Cacheable缓存的结果都是方法的返回值
属性 | 说明 | 示例 |
---|---|---|
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个,如果是多个,则将结果写入多个缓存 | @Cacheable(value={”cache1”,”cache2”} |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 | @CacheCacheable(value="userCache", key="#user.id" ) |
condition | 满足缓存条件的数据才会放入缓存,condition在调用方法之前和之后都会判断 | @CacheCacheable(value="userCache",condition="#user.id.length()>4" ) |
unless | 满足条件不更新缓存,不像condition,该表达只在方法执行之后判断,此时可以拿到返回值result进行判断了 | @CacheCacheable(value="userCache",condition="#user.id.length()>4" )//满足条件不更新缓存 |
@CacheEvict
清理缓存。根据根据key清理缓存;清理所有缓存;根据条件清理缓存
属性 | 说明 | 示例 |
---|---|---|
value | 同CachePut | |
key | 同CachePut | |
condition | 同CachePut | |
allEntries | 是否清理所有缓存数据 | @CacheEvict(value="userCache",allEntries=true) |
beforeInvocation | 是否在方法执行之前清理 | @CacheEvict(value="userCache",beforeInvocation=false) |
三种方式的对比
-
@Cacheable 根据请求参数缓存结果(方法返回值),在方法执行前调用;
-
@CachePut 根据请求参数缓存结果(方法返回值),在方法执行后调用;
-
@CachEvict 清理缓存,可以根据请求参数清理,也可以全部清理;可在方法执行前或后清理缓存;
注意
基于动态代理的实现,所以在对象内部调用时缓存不生效