SpringBoot缓存注解的使用

简介: Spring从3.1版本开始定义了两个接口来统一不同的缓存从而帮助简化开发过程。它们分别是:org.springframework.cache.Cache和org.springframework.cache.CacheManager接口。

前言


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
复制代码


查询结果如下:


微信截图_20220521201058.png


这时控制台打印日志:


微信截图_20220521201006.png


重新刷新页面,请求查询,结果如下:


微信截图_20220521201058.png


这时控制台则没有任何打印日志信息, 则表示查询是通过缓存。


关于**@****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注解开启缓存,然后在方法上根据需要使用具体的缓存注解即可。


如有任何问题或者不对的地方欢迎一起交流讨论学习!

目录
相关文章
|
2月前
|
Java 开发者 Spring
【SpringBoot 异步魔法】@Async 注解:揭秘 SpringBoot 中异步方法的终极奥秘!
【8月更文挑战第25天】异步编程对于提升软件应用的性能至关重要,尤其是在高并发环境下。Spring Boot 通过 `@Async` 注解简化了异步方法的实现。本文详细介绍了 `@Async` 的基本用法及配置步骤,并提供了示例代码展示如何在 Spring Boot 项目中创建与管理异步任务,包括自定义线程池、使用 `CompletableFuture` 处理结果及异常情况,帮助开发者更好地理解和运用这一关键特性。
125 1
|
2月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
|
2月前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
2月前
|
缓存 Java 数据库连接
Spring Boot奇迹时刻:@PostConstruct注解如何成为应用初始化的关键先生?
【8月更文挑战第29天】作为一名Java开发工程师,我一直对Spring Boot的便捷性和灵活性着迷。本文将深入探讨@PostConstruct注解在Spring Boot中的应用场景,展示其在资源加载、数据初始化及第三方库初始化等方面的作用。
53 0
|
9天前
|
Java Spring 容器
Spring使用异步注解@Async正确姿势
Spring使用异步注解@Async正确姿势,异步任务,spring boot
|
8天前
|
XML Java 数据格式
spring复习03,注解配置管理bean
Spring框架中使用注解配置管理bean的方法,包括常用注解的标识组件、扫描组件、基于注解的自动装配以及使用注解后的注意事项,并提供了一个基于注解自动装配的完整示例。
spring复习03,注解配置管理bean
|
9天前
|
XML 前端开发 Java
控制spring框架注解介绍
控制spring框架注解介绍
|
22天前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
|
2月前
|
Java 数据安全/隐私保护 Spring
揭秘Spring Boot自定义注解的魔法:三个实用场景让你的代码更加优雅高效
揭秘Spring Boot自定义注解的魔法:三个实用场景让你的代码更加优雅高效
|
2月前
|
XML Java 数据库
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
这篇文章是Spring5框架的实战教程,详细介绍了事务的概念、ACID特性、事务操作的场景,并通过实际的银行转账示例,演示了Spring框架中声明式事务管理的实现,包括使用注解和XML配置两种方式,以及如何配置事务参数来控制事务的行为。
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
下一篇
无影云桌面