Spring-cache的抽象
来源:互联网 发布:淘宝c店详情页banner 编辑:程序博客网 时间:2024/05/22 01:27
背景
前面已经分析了SpringBoot的一套starter和autoconfigure 的机制,具体参考:http://blog.csdn.net/u010853261/article/details/77961716 这篇博客。
其实对于SpringBoot的starter机制,其实就是依据SpringBoot的约定大于配置的思想,启动SpringBoot时候,根据依赖的starter,就已经自动装载了很多bean,也就是开箱即用,不需要用户自己配置。
缓存在我们的应用系统中也是应用的很多的地方,而且现在也有各种各样的缓存,有单机的ConcurrentHashMap、guava的cache、ehcache、以及分布式的redis、tair等等,Spring通过一个Spring-boot-cache-starter的模块,帮我们抽象的封装了底层具体是哪种cache的实现。 也就是我们只需要使用Spring cache就行了,而不用关心底层是哪一种cache.(当然,底层肯定是需要配置具体缓存的starter的)
那么这种抽象的封装是怎么实现的呢?
1. Spring cache的抽象实现
在Spring中定义了两个接口org.springframework.cache.Cache
和org.springframework.cache.CacheManager
,来实现对cache和cache管理的封装。Cache就是具体的缓存读取对象,提供最基本的put、get、evict等操作。CacheManager则是具体的Cache的一个manager对象,用来管理具体的每个Cache对象, 其实也就是类似于一个Cache的factory。
我们先来看看这两个接口的能力:
Cache.java
public interface Cache { /**Return the cache name.*/ String getName(); /**Return the underlying native cache provider.*/ Object getNativeCache(); /**Return the value to which this cache maps the specified key.*/ ValueWrapper get(Object key); <T> T get(Object key, Class<T> type); /** * Return the value to which this cache maps the specified key, obtaining * that value from {@code valueLoader} if necessary. This method provides * a simple substitute for the conventional "if cached, return; otherwise * create, cache and return" pattern. */ <T> T get(Object key, Callable<T> valueLoader); /** * Associate the specified value with the specified key in this cache. */ void put(Object key, Object value); /** * Atomically associate the specified value with the specified key in this cache * if it is not set already. */ ValueWrapper putIfAbsent(Object key, Object value); /** * Evict the mapping for this key from this cache if it is present. */ void evict(Object key); /** * Remove all mappings from the cache. */ void clear(); interface ValueWrapper { Object get(); } @SuppressWarnings("serial") class ValueRetrievalException extends RuntimeException { private final Object key; public ValueRetrievalException(Object key, Callable<?> loader, Throwable ex) { super(String.format("Value for key '%s' could not be loaded using '%s'", key, loader), ex); this.key = key; } public Object getKey() { return this.key; } }}
CacheManager.java
public interface CacheManager { /** * Return the cache associated with the given name. */ Cache getCache(String name); /** * Return a collection of the cache names known by this manager. */ Collection<String> getCacheNames();}
我们看看这两个接口在spring-context的实现:
cache的实现
CacheManager的实现
在没有SpringBoot的时候,我们需要自己配置注入CacheManager,然后才能够使用cache。但是SpringBoot已经帮我们解决了这个问题,通过自动装配,默认帮我们注入这个cache。
我们看看SpringBoot的autoconfigure包下面关于cache的自动装配的实现:
可以知道,对于一些常见的缓存,已经帮我们做了实现:比如guava, redis, concurrentHashMap等等。
当我们引入了相应的依赖包时,这个cacheManager就会被自动装配。在不适用任何额外配置的情况下,默认使用SimpleCacheConfiguration,也就是基于ConcurrentMap.
下面我们以一个最简单的实例来说明:SimpleCacheConfiguration
SimpleCacheConfiguration的实现
我们先看看SimpleCacheConfiguration里面最核心的代码:
@Beanpublic ConcurrentMapCacheManager cacheManager() { ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); List<String> cacheNames = this.cacheProperties.getCacheNames(); if (!cacheNames.isEmpty()) { cacheManager.setCacheNames(cacheNames); } return this.customizerInvoker.customize(cacheManager);}
这段代码:
- 生成了ConcurrentMapCacheManager(cacheManager的实现类)的bean。
- 根据application.properties里面配置的信息,生成其CacheManager的bean
在ConcurrentMapCacheManager里面维护这一个CacheName到cache的Map:
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<String, Cache>(16);
然后对于以ConcurrentMap作为底层存储的Cache实现是:ConcurrentMapCache,底层维护着具体的存储载体:
private final ConcurrentMap<Object, Object> store;
这就是大概的实现过程。对于其余的存储方式也是类似的。
Tair的实现
在tair的Spring-boot-tair-starter里面的autoconfigure里面,TairManager的实现并没有依赖于Spring的CacheManager, 而Spring也没有提供正式的autoConfig给我们,所以对于tair的集成使用Spring cache需要我们自己实现。 集团的pandora-boot-tair-spring-boot-autoconfigurate也只是注入了tair的TairManager, 这个与Spring的cache并没有什么关系。所以如果我们如果要通过SpringCache集成tair, 就必须自己手动写插件。
这里我们必须实现的其实就是类似于基于tair的TairManager(已注入), 然后实现一个Spring Cache的插件。
Spring cache 基于注解的使用
使用说明
1. 引入依赖
(1)maven依赖:包括spring-boot-starter-cache(必须)、第三方缓存技术依赖(可选),比如Redis等等
(2)Springboot的配置类上加上@EnableCaching注解开启缓存功能
(3)在数据访问接口中,增加缓存配置注解。比如下例子:
@CacheConfig(cacheNames = "users")public interface UserRepository extends JpaRepository<User, Long> { @Cacheable User findByName(String name);}
常用注解如下:
(1)@CacheConfig:主要用于配置该类中会用到的一些共用的缓存配置。在这里比如@CacheConfig(cacheNames = “users”):配置了该数据访问对象中返回的内容将存储于名为users的缓存对象中,我们也可以不使用该注解,直接通过@Cacheable自己配置缓存集的名字来定义。
(2)@Cacheable:配置了findByName函数的返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问。该注解主要有下面几个参数:
value、cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了
key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:
@Cacheable(key = "#p0")
:使用函数第一个参数作为缓存的key值,更多关于SpEL表达式的详细内容可参考官方文档condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:
@Cacheable(key = "#p0", condition = "#p0.length() < 3")
,表示只有当第一个参数的长度小于3的时候才会被缓存,若做此配置上面的AAA用户就不会被缓存,读者可自行实验尝试。unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。
keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。需要注意的是:该参数与key是互斥的
cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用
cacheResolver:用于指定使用那个缓存解析器,非必需。需通过org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定。
除了这里用到的两个注解之外,还有下面几个核心注解:
(1)@CachePut:配置于函数上,能够根据参数定义条件来进行缓存,它与@Cacheable不同的是,它每次都会真是调用函数,所以主要用于数据新增和修改操作上。它的参数与@Cacheable类似,具体功能可参考上面对@Cacheable参数的解析
(2)@CacheEvict:配置于函数上,通常用在删除方法上,用来从缓存中移除相应数据。除了同@Cacheable一样的参数之外,它还有下面两个参数:
* allEntries:非必需,默认为false。当为true时,会移除所有数据
* beforeInvocation:非必需,默认为false,会在调用方法之后移除数据。当为true时,会在调用方法之前移除数据。
- Spring-cache的抽象
- Spring Cache抽象详解
- Spring Cache抽象详解
- Spring Cache抽象详解
- Spring Cache抽象详解
- Spring Cache抽象详解
- Spring Cache抽象详解
- Spring Cache抽象详解
- Spring Cache抽象详解
- Spring Cache抽象详解
- Spring Cache抽象详解
- Spring Cache抽象详解
- Spring Cache抽象详解
- Spring Cache抽象详解
- Spring Cache抽象详解
- Spring Cache抽象详解
- Spring Cache抽象详解
- Spring Cache抽象-缓存注解
- 管好你的时间只需要做到这3点
- 虚拟化笔记05 OpenFiler configuration
- Docker核心概念
- bzoj3143 [Hnoi2013]游走 (期望概率DP + 高斯消元)
- 单链表逆置
- Spring-cache的抽象
- 防止表单重复提交token机制
- Thinkphp3.2.3关联模型(总结)
- java反编译javap
- [SDUT](3334)数据结构实验之栈七:出栈序列判定 ---栈
- Jmeter之JDBC Request使用方法(oracle)
- https
- Java中数组和List的转换
- C++定时器