前言
Spring从3.1版本开始定义了两个接口来统一不同的缓存从而帮助简化开发过程。它们分别是:
org.springframework.cache.Cache和org.springframework.cache.CacheManager接口。
其中Cache接口为缓存的组件规范定义,包含缓存的各种操作(增删改查)集合。Cache接口下Spring提供了各种缓存的实现,比如RedisCache、EhCacheCache、ConcurrentMapCache等。每次调用需要缓存功能的方法时,Spring会检查指定参数的指定的目标方法是否已经被调用过,如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。使用Spring缓存抽象时我们需要关注以下两点:
1、确定方法需要被缓存以及缓存策略
2、从缓存中读取之前缓存存储的数据
今天就来学习SpringBoot缓存中缓存注解的使用。
这篇文章项目的基础是前一篇文章的项目,可以参考:SpringBoot多数据源配置,源码会贴在最后。
正文
Springboot的缓存抽象中有几个重要的注解,,它们分别是
@EenableCacheing:该注解主要用于开启基于注解的缓存支持,用在Application类上
@CacheEvict:该注解用于清理缓存
@CachePut:该注解用于设置缓存
@Caching:该注解可以对缓存清理、设置 等操作打包
@CacheConfig:该注解同样用于缓存设置
@Cacheable:该注解主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,比如如果缓存在存在该值,则用缓存数据, 如果不在缓存,则存入缓存;
Springboot使用缓存抽象,首先需要在Application类上加@EnableCaching注解开启缓存。比如:
@SpringBootApplication @MapperScan("com.springboot.springbootdemo.dao") @EnableCaching public class SpringbootdemoApplication { private static final Logger logger = LoggerFactory.getLogger(SpringbootdemoApplication.class); public static void main(String[] args) { SpringApplication.run(SpringbootdemoApplication.class, args); } } 复制代码
开启缓存之后,在service的方法上使用**@****Cacheable**注解进行缓存,比如:
@Cacheable(cacheNames = "dep") public Department findByID(int id) { System.out.println("查询的数据是ID为"+id+"的数据"); return departmentDao.findById(id); } 复制代码
cacheNames属性表示指定缓存的名字,缓存使用CacheManager管理多个缓存组件Cache,这些Cache组件就是根据这个名字进行区分的。对缓存的真正CRUD操作在Cache中定义,每个缓存组件Cache都有自己唯一的名字,通过cacheNames或者value属性指定,相当于是将缓存的键值对进行分组,缓存的名字是一个数组,也就是说可以将一个缓存键值对分到多个组里面。
启动项目,成功之后地址栏输入:
http://localhost:8081/share/do/department/dep?id=1 复制代码
查询结果如下:
这时控制台打印日志:
重新刷新页面,请求查询,结果如下:
这时控制台则没有任何打印日志信息, 则表示查询是通过缓存。
关于**@****Cacheable**注解源码如下,可以发现其有几个重要的属性,属性具体含义在注解翻译:
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Cacheable { /** * Alias for {@link #cacheNames}. */ @AliasFor("cacheNames") String[] value() default {}; /** * Names of the caches in which method invocation results are stored. * <p>Names may be used to determine the target cache (or caches), matching * the qualifier value or bean name of a specific bean definition. * @since 4.2 * @see #value * @see CacheConfig#cacheNames * cacheNames/value指定缓存组件的名字 */ @AliasFor("value") String[] cacheNames() default {}; /** * Spring Expression Language (SpEL) expression for computing the key dynamically. * <p>Default is {@code ""}, meaning all method parameters are considered as a key, * unless a custom {@link #keyGenerator} has been configured. * <p>The SpEL expression evaluates against a dedicated context that provides the * following meta-data: * <ul> * <li>{@code #root.method}, {@code #root.target}, and {@code #root.caches} for * references to the {@link java.lang.reflect.Method method}, target object, and * affected cache(s) respectively.</li> * <li>Shortcuts for the method name ({@code #root.methodName}) and target class * ({@code #root.targetClass}) are also available. * <li>Method arguments can be accessed by index. For instance the second argument * can be accessed via {@code #root.args[1]}, {@code #p1} or {@code #a1}. Arguments * can also be accessed by name if that information is available.</li> * </ul> * 缓存数据时使用的key,可以使用它来指定,默认是使用方法参数的值. * 可以使用Spring Expression Language (SpEL) expression表达式编写 */ String key() default ""; /** * The bean name of the custom {@link org.springframework.cache.interceptor.KeyGenerator} * to use. * <p>Mutually exclusive with the {@link #key} attribute. * @see CacheConfig#keyGenerator * key的生成器:可以自己指定key的生成器的组件id * key/keyGenerator 二选一使用 */ String keyGenerator() default ""; /** * The bean name of the custom {@link org.springframework.cache.CacheManager} to use to * create a default {@link org.springframework.cache.interceptor.CacheResolver} if none * is set already. * <p>Mutually exclusive with the {@link #cacheResolver} attribute. * @see org.springframework.cache.interceptor.SimpleCacheResolver * @see CacheConfig#cacheManager * 管理多个cache组件,对缓存真正的crud操作上是在 * cache组件中,每一个缓存组件都有自己唯一的名字; */ String cacheManager() default ""; /** * The bean name of the custom {@link org.springframework.cache.interceptor.CacheResolver} * to use. * @see CacheConfig#cacheResolver * 缓存解析器和cacheManager功能一样,和cacheManager二选一 */ String cacheResolver() default ""; /** * Spring Expression Language (SpEL) expression used for making the method * caching conditional. * <p>Default is {@code ""}, meaning the method result is always cached. * <p>The SpEL expression evaluates against a dedicated context that provides the * following meta-data: * <ul> * <li>{@code #root.method}, {@code #root.target}, and {@code #root.caches} for * references to the {@link java.lang.reflect.Method method}, target object, and * affected cache(s) respectively.</li> * <li>Shortcuts for the method name ({@code #root.methodName}) and target class * ({@code #root.targetClass}) are also available. * <li>Method arguments can be accessed by index. For instance the second argument * can be accessed via {@code #root.args[1]}, {@code #p1} or {@code #a1}. Arguments * can also be accessed by name if that information is available.</li> * </ul> * 缓存条件指定缓存的条件(满足什么条件时才缓存),可用SpEL表达式(如#id>0,表示当入参id大于0时才缓存) */ String condition() default ""; /** * Spring Expression Language (SpEL) expression used to veto method caching. * <p>Unlike {@link #condition}, this expression is evaluated after the method * has been called and can therefore refer to the {@code result}. * <p>Default is {@code ""}, meaning that caching is never vetoed. * <p>The SpEL expression evaluates against a dedicated context that provides the * following meta-data: * <ul> * <li>{@code #result} for a reference to the result of the method invocation. For * supported wrappers such as {@code Optional}, {@code #result} refers to the actual * object, not the wrapper</li> * <li>{@code #root.method}, {@code #root.target}, and {@code #root.caches} for * references to the {@link java.lang.reflect.Method method}, target object, and * affected cache(s) respectively.</li> * <li>Shortcuts for the method name ({@code #root.methodName}) and target class * ({@code #root.targetClass}) are also available. * <li>Method arguments can be accessed by index. For instance the second argument * can be accessed via {@code #root.args[1]}, {@code #p1} or {@code #a1}. Arguments * can also be accessed by name if that information is available.</li> * </ul> * @since 3.2 * 否定缓存和condition效果相反,即满足unless指定的条件时,方法的结果不进行缓存,使用unless时可以在调用的方法获取到结果之后再进行判断(如#result==null,表示如果结果为null时不缓存) * 既满足condition又满足unless条件的也不进行缓存 */ String unless() default ""; /** * Synchronize the invocation of the underlying method if several threads are * attempting to load a value for the same key. The synchronization leads to * a couple of limitations: * <ol> * <li>{@link #unless()} is not supported</li> * <li>Only one cache may be specified</li> * <li>No other cache-related operation can be combined</li> * </ol> * This is effectively a hint and the actual cache provider that you are * using may not support it in a synchronized fashion. Check your provider * documentation for more details on the actual semantics. * @since 4.3 * @see org.springframework.cache.Cache#get(Object, Callable) * 是否使用异步模式进行缓存 * 使用异步模式进行缓存时(sync=true):unless条件将不被支持 */ boolean sync() default false; } 复制代码
@CachePut:该注解用于设置缓存,表示在调用方法的同时又更新缓存,它是默认先调用目标方法,然后将目标方法的运行结果存入缓存,不过需要注意的是如果要保持缓存同步更新,这个注解使用的key需要和缓存的key保持一致,和@cacheable不同的是@cacheable不同是在运行在目标方法之前,而它是目标方法之后,因为它需要先拿到目标方法运行的结果。
@Cacheable不可以使用#result,因为使用@Cacheable标注的方法不一定会被调用,可能获取不到result。它的属性和@cacheable基本一致,就少了一个是否异步(sync)的属性,它的使用如下:
@CachePut(value="dep",key="#department.id") public Department updateDepartment(Department department){ System.out.println("更新信息:"+department); departmentDao.updateDepartment(department); return department; } 复制代码
@CacheEvict:该注解用于缓存清除,清除缓存时要指明缓存的名字和key,相当于告诉数据库要删除哪个表中的哪条数据,key默认为参数的值。主要属性除了value/cacheNames和key之外还有allEntries和beforeInvocation属性。篇幅有限源码不贴了。
allEntries表示是否清除指定缓存中的所有键值对,既是否清除所有缓存,设置为true时会清除缓存中的所有键值对,默认为false,即根据key清除缓存。所以它与key属性二选一使用。
beforeInvocation表示是否是在@CacheEvict注解的方法调用之前清除指定缓存,默认为false,即在方法调用之后清除缓存,设置为true时则会在方法调用之前清除缓存。在方法调用之前还是之后清除缓存的区别在于方法调用时是否会出现异常,若不出现异常,这两种设置没有区别,若出现异常,设置为在方法调用之后清除缓存将不起作用,因为方法调用失败了。
用法如下:
@CacheEvict(value = "dep",key = "#id"/*,beforeInvocation = true*//*,allEntries = true*/) public boolean delDep(int id){ boolean flag=false; try{ departmentDao.deleteDepartmentById(id); flag=true; }catch(Exception e){ e.printStackTrace(); } return flag; } 复制代码
@Caching:该注解可以对缓存清除、设置等操作打包,即是@Cacheable、@CachePut、@CacheEvict的组合,来定义复杂的缓存规则,在这个组合中只要有@CachePut就一定会调用被注解的方法,通过它的源码就很清楚:
public @interface Caching { Cacheable[] cacheable() default {}; CachePut[] put() default {}; CacheEvict[] evict() default {}; } 复制代码
具体使用如下:
@Caching( cacheable = { @Cacheable(value = "user", key = "#userName") }, put = { @CachePut(value = "user", key = "#result.id"), @CachePut(value = "user", key = "#result.email") } ) public User findUserByName(String userName) { System.out.println("查询的是:"+userName); return userDao.findByName(userName); } 复制代码
@CacheConfig:该注解同样用于缓存设置,标注在类上,即可抽取缓存相关注解的公共配置,可抽取的公共配置有缓存名字、主键生成器等,比如上面其他注解的cachename,key等,源码如下:
public @interface CacheConfig { /** * Names of the default caches to consider for caching operations defined * in the annotated class. * <p>If none is set at the operation level, these are used instead of the default. * <p>May be used to determine the target cache (or caches), matching the * qualifier value or the bean names of a specific bean definition. */ String[] cacheNames() default {}; /** * The bean name of the default {@link org.springframework.cache.interceptor.KeyGenerator} to * use for the class. * <p>If none is set at the operation level, this one is used instead of the default. * <p>The key generator is mutually exclusive with the use of a custom key. When such key is * defined for the operation, the value of this key generator is ignored. */ String keyGenerator() default ""; /** * The bean name of the custom {@link org.springframework.cache.CacheManager} to use to * create a default {@link org.springframework.cache.interceptor.CacheResolver} if none * is set already. * <p>If no resolver and no cache manager are set at the operation level, and no cache * resolver is set via {@link #cacheResolver}, this one is used instead of the default. * @see org.springframework.cache.interceptor.SimpleCacheResolver */ String cacheManager() default ""; /** * The bean name of the custom {@link org.springframework.cache.interceptor.CacheResolver} to use. * <p>If no resolver and no cache manager are set at the operation level, this one is used * instead of the default. */ String cacheResolver() default ""; } 复制代码
具体使用如下:
@Service @CacheConfig(cacheNames = "user") public class UserServiceImpl implements UserService { .... } 复制代码
总结
在springboot中使用缓存注解很简单,首先就只需要在依赖中引入spring-boot-starter-cache依赖,然后在application类中使用@EnableCaching注解开启缓存,然后在方法上根据需要使用具体的缓存注解即可。
如有任何问题或者不对的地方欢迎一起交流讨论学习!