spring-cache-1

Spring的缓存技术:
源码分析:
interface ConcurrentMap<K, V> extends Map<K, V>  <--实现-- class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>, Serializable  //并发map集合

用户所存储缓存的数据池: class ConcurrentMapCache extends AbstractValueAdaptingCache
    private final ConcurrentMap<Object, Object> store; //实际存储对象
    T get(Object key, Callable<T> valueLoader)         //获取指定key的缓存
    void put(Object key, Object value)                 //存储一个缓存

abstract class AbstractValueAdaptingCache implements Cache  //所属缓存类型

Spring框架运行时需要的抽象的缓存技术的声明: interface Cache{
    String getName();                       //缓存的名字
    Object getNativeCache();                //得到底层使用的缓存,如Ehcache
    ValueWrapper get(Object key);           //根据key得到一个ValueWrapper,然后调用其get方法获取值
    <T> T get(Object key, Class<T> type);   //根据key,和value的类型直接获取value
    void put(Object key, Object value);     //往缓存放数据
    void evict(Object key);                 //从缓存中移除key对应的缓存
    void clear();                           //清空缓存
    interface ValueWrapper {                //缓存值的Wrapper
        Object get();                       //得到真实的value
    }
}

Spring框架运行时需要的抽象的一组缓存技术的声明: interface CacheManager {
   Cache getCache(String name);            //单个缓存抽象声明
   Collection<String> getCacheNames();     //所有缓存技术的类型的名称
}

并发封装:          class ConcurrentMapCacheManager     implements CacheManager, BeanClassLoaderAware //解决并发导致的问题

重量级工厂并发封装: class ConcurrentMapCacheFactoryBean implements FactoryBean<ConcurrentMapCache>, BeanNameAware, InitializingBean //提供构建方法而无需每次都创建

组合封装:          class CompositeCacheManager          implements CacheManager, InitializingBean //用来组合多种类型的缓存并轮询从多种缓存源中获取数据

一次设置缓存的过程: ConcurrentMapCacheManager.getCache("缓存技术类型名称")(ConcurrentMapCache).get[ConcurrentMap](name);
一次设置取出的过程: ConcurrentMapCacheManager.getCache("缓存技术类型名称")(ConcurrentMapCache).put[ConcurrentMap](name,value);


开发者使用时:
@CachePut:应用到写数据的方法上,如新增/修改方法,调用方法时会自动把相应的数据放入缓存,根据返回值按照形参值进行添加/修改
比如说:save/update
@interface CachePut {
    String[] value();              //缓存的名字,可以把数据写到多个缓存,别名
    String key() default "";       //缓存key,如果不指定将使用默认的KeyGenerator生成,后边介绍
    String condition() default ""; //满足缓存条件的数据才会放入缓存,condition在调用方法之前和之后都会判断
    String unless() default "";    //用于否决缓存更新的,不像condition,该表达只在方法执行之后判断,此时可以拿到返回值result进行判断了
}
@CacheEvict:即应用到移除数据的方法上,如删除方法,调用方法时会从缓存中移除相应的数据,按照形参值进行删除
@interface CacheEvict {
    String[] value();                        //缓存的名字,可以把数据写到多个缓存,别名
    String key() default "";                 //缓存key,如果不指定将使用默认的KeyGenerator生成,后边介绍
    String condition() default "";           //满足缓存条件的数据才会清理缓存,condition在调用方法之前和之后都会判断
    boolean allEntries() default false;      //是否移除所有数据
    boolean beforeInvocation() default false;//是调用方法之前移除/还是调用之后移除
比如说:delete/deleteAll
@Cacheable:应用到读取数据的方法上,即可缓存的方法,如查找方法:先从缓存中读取,如果没有再调用方法获取数据,然后把数据添加到缓存中:
public @interface Cacheable {
    String[] value();             //缓存的名字,可以把数据写到多个缓存,别名
    String key() default "";      //缓存key,如果不指定将使用默认的KeyGenerator生成,后边介绍
    String condition() default "";//满足缓存条件的数据才会放入取出缓存,condition在调用方法之前和之后都会判断
    String unless() default "";   //用于否决缓存更新的,不像condition,该表达只在方法执行之后判断,此时可以拿到返回值result进行判断了
比如说:get/search
运行流程:
    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,则清空所有
注意:
    在同一个业务方法上,如果有@CachePut操作,即使有@Cacheable也不会从缓存中读取;
    问题很明显,如果要混合多个注解使用,不能组合使用@CachePut和@Cacheable;
    官方说应该避免这样使用(解释是如果带条件的注解相互排除的场景);
Key生成器:
    如果在Cache注解上没有指定key的话@CachePut(value = "user"),spring默认会使用KeyGenerator进行生成一个key:
功能描述:interface KeyGenerator {
   Object generate(Object target, Method method, Object... params);
}
具体实现:class SimpleKeyGenerator implements KeyGenerator {
   @Override
   public Object generate(Object target, Method method, Object... params) {
      return generateKey(params);
   }
   public static Object generateKey(Object... params) {
      if (params.length == 0) { //如果参数的长度为0,使用的是同一个simpleKey
         return SimpleKey.EMPTY;
      }
      if (params.length == 1) { //如果参数的个数为1个
         Object param = params[0];
         if (param != null && !param.getClass().isArray()) {
            return param;
         }
      }
      return new SimpleKey(params);
   }
}
key: class SimpleKey implements Serializable{
    public static final SimpleKey EMPTY = new SimpleKey();
    private final Object[] params;
    private final int hashCode;
    SimpleKey(Object... elements) {
        this.params = new Object[elements.length];
        System.arraycopy(elements, 0, this.params, 0, elements.length);
        this.hashCode = Arrays.deepHashCode(this.params);
    }
}
另外:
    System下的static native void arraycopy(Object src,  int  srcPos,Object dest, int destPos,int length); 拷贝源数组中指定起始位置到结尾的数据到目标数组的开始位置到源数组的长度
SpEL上下文:
    | **名字**          | **位置** | **描述**                                   | **示例**                 |
    | --------------- | ------ | ---------------------------------------- | ---------------------- |
    | methodName      | root对象 | 当前被调用的方法名                                | `#root.methodName`     |
    | method          | root对象 | 当前被调用的方法                                 | `#root.method.name`    |
    | target          | root对象 | 当前被调用的目标对象                               | `#root.target`         |
    | targetClass     | root对象 | 当前被调用的目标对象类                              | `#root.targetClass`    |
    | args            | root对象 | 当前被调用的方法的参数列表                            | `#root.args[0]`        |
    | caches          | root对象 | 当前方法调用使用的缓存列表(如@Cacheable(value={"cache1", "cache2"})),则有两个cache | `#root.caches[0].name` |
    | *argument name* | 执行上下文  | 当前被调用的方法的参数,如findById(Long id),我们可以通过#id拿到参数 | #user.id               |
    | result          | 执行上下文  | 方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,'cache evict'的beforeInvocation=false) | `#result`              |
    ...
    通过#对象.属性名获取spEL值,用于condition中做条件判断
条件判断(condition):
    例如: @Cacheable(condition = "#id lt 10")
    常用:
        lt:小于,gt:大于,ne:值不等,eq:值相等
    当condition表达式的值为true时执行缓存相关操作
    当unless表达式的值为false是执行缓存相关操作
    常与beforeInvocation=false属性一起使用,为true表示在方法执行之前执行,为false表示在方法之前之后执行
组合注解: @Caching:
@Caching(put = {},evict = {},cacheable = {})

猜你喜欢

转载自blog.csdn.net/qq_35559756/article/details/78541490