【spring】spring cache 使用详解

Spring缓存抽象

Spring从3.1开始定义了一系列抽象接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们进行缓存开发。Spring Cache 只负责维护抽象层,具体的实现由你的技术选型来决定。将缓存处理和缓存技术解除耦合。

依赖引入

Spring cache 抽象由spring-context相关组件实现。非Spring Boot 项目可通过引入该模块进行集成。

Spring Boot 项目可引入以下依赖:


        
            org.springframework.boot
            spring-boot-starter-cache
        

同时可能需要引入你采用的缓存中间件客户端;比如 Ehcache、redis等。

两个重要抽象概念

  • Cache 缓存抽象规范接口,定义缓存一些了操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等
  • CacheManager 缓存管理器,管理Cache的生命周期。

常用的一些注解

Spring cache 提供了一系列的注解,将我们从编程开发中解放出来。让我们更加关注于业务开发。

@EnableCaching

该注解是启用Spring cache 的开关。必须开启才能使用Spring cache相关功能。

@CacheConfig

作用于缓存接口上,来对该接口下的一些重复配置(缓存名称、key生成器、缓存管理器、缓存处理器)进行归纳处理。其他属性可参考Cacheable。

@Cacheable(类,方法)

可以标记在一个方法或者类上。方法级只针对该方法。类上则针对类内所有的方法。在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果。


public @interface Cacheable {

    @AliasFor("cacheNames")
    String[] value() default {};
     //和value注解差不多,二选一
    String[] cacheNames() default {}; 
    // 该次缓存的key 
    String key() default "";
    //key的生成器。key/keyGenerator二选一使用
    String keyGenerator() default "";
    //指定缓存管理器 一般使用默认
    String cacheManager() default ""; 
    //或者指定获取解析器  一般使用默认
    String cacheResolver() default ""; 
    //条件符合则缓存 使用的比较多 支持SpEL
    String condition() default ""; 
    //条件符合则不缓存 使用的比较多 支持SpEL
    String unless() default ""; 
    //是否使用异步模式
    boolean sync() default false; 
}

后面有个别注解属性跟这个基本相同不进行重复介绍。

key/keyGenerator

  • SpringEL表达式

       1.#参数名”或者“#p参数index”

@Cacheable(value="users", key="#id")

   public User find(Integer id) {

      returnnull;

   }

   @Cacheable(value="users", key="#p0")

   public User find(Integer id) {

      returnnull;

   }

   @Cacheable(value="users", key="#user.id")

   public User find(User user) {

      returnnull;

   }

   @Cacheable(value="users", key="#p0.id")

   public User find(User user) {

      returnnull;

   }

       2.Spring还为我们提供了一个root对象可以用来生成key

            

  • Spring默认的SimpleKeyGenerator

        Spring默认的SimpleKeyGenerator是不会将函数名组合进key中的

  • 自定义策略
@Bean
    public KeyGenerator KeyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
@Cacheable(value="gomeo2oCache", keyGenerator = "keyGenerator")
public ResultDTO method(User user);

@CachePut(类,方法)

public @interface CachePut {  
    String[] value();              //缓存的名字,可以把数据写到多个缓存  
    String key() default "";       //缓存key,如果不指定将使用默认的KeyGenerator生成,后边介绍  
    String condition() default ""; //满足缓存条件的数据才会放入缓存,condition在调用方法之前和之后都会判断  
    String unless() default "";    //用于否决缓存更新的,不像condition,该表达只在方法执行之后判断,此时可以拿到返回值result进行判断了  
}

该注解容易跟@Cacheable混淆。两者都可以执行缓存的“放入”操作,不同于@Cacheable,@CachePut每在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中,如果该K存在则进行更新。其他属性可参考Cacheable。

@CacheEvict(类,方法)

public @interface CacheEvict {  
    String[] value();                        //请参考@CachePut  
    String key() default "";                 //请参考@CachePut  
    String condition() default "";           //请参考@CachePut  
    boolean allEntries() default false;      //是否移除所有数据  
    boolean beforeInvocation() default false;//是调用方法之前移除/还是调用之后移除

@CachEvict主要针对方法配置,能够根据一定的条件对特定的缓存进行清空。该注解有两个特别的属性:

  • value 表示清除操作是发生在哪些Cache上的(对应Cache的名称)
  • key 表示需要清除的是哪个key,如未指定则会使用默认策略生成的key
  • condition 表示清除操作发生的条件
  • allEntries 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存。注意不能跟key参数同时使用。
  • beforeInvocation 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存。

@Caching

  • cacheable
  • put 
  • evict

该注解是个组合注解。有时候我们需要在一个方法上同时使用多个相同注解但是java是不支持一个注解在同一个方法上多次使用。这时就可以使用该注解进行组合。

@Caching(cacheable = @Cacheable("users"), evict = { @CacheEvict("cache2"),
@CacheEvict(value = "cache3", allEntries = true) })
   public User find(Integer id) {
      return null;
   }

运行流程

  1. 首先执行@CacheEvict(如果beforeInvocation=true且condition 通过),如果allEntries=true,则清空所有  
  2. 接着收集@Cacheable(如果condition 通过,且key对应的数据不在缓存),放入cachePutRequests(也就是说如果cachePutRequests为空,则数据在缓存中)  
  3. 如果cachePutRequests为空且没有@CachePut操作,那么将查找@Cacheable的缓存,否则result=缓存数据(也就是说只要当没有cache put请求时才会查找缓存)  
  4. 如果没有找到缓存,那么调用实际的API,把结果放入result  
  5. 如果有@CachePut操作(如果condition 通过),那么放入cachePutRequests  
  6. 执行cachePutRequests,将数据写入缓存(unless为空或者unless解析结果为false);  
  7. 执行@CacheEvict(如果beforeInvocation=false 且 condition 通过),如果allEntries=true,则清空所有  

流程中需要注意的就是2/3/4步:

如果有@CachePut操作,即使有@Cacheable也不会从缓存中读取;问题很明显,如果要混合多个注解使用,不能组合使用@CachePut和@Cacheable;官方说应该避免这样使用(解释是如果带条件的注解相互排除的场景)。

自定义注解

Spring允许我们在配置可缓存的方法时使用自定义的注解,前提是自定义的注解上必须使用对应的注解进行标注。如我们有如下这么一个使用@Cacheable进行标注的自定义注解。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Cacheable(value="users")
public @interface MyCacheable {
}
@MyCacheable
   public User findById(Integer id) {
      System.out.println("find user by id: " + id);
      User user = new User();
      user.setId(id);
      user.setName("Name" + id);
      return user;
   }

使用要点

  • 缓存注解所在的方法不能在类中进行内部调用。
  • 缓存一定要有过期超时策略,避免系统不堪重负。
  • 缓存的值如果是集合考虑对集合的大小的限制,避免序列化/反序列化性能。

你可能感兴趣的:(◆,spring,◆)