简介
- 自Spring 3.1起,提供了基于注解的Cache支持(之前是用aop实现的),且提供了Cache抽象。
- Spring Cache优点:
- 提供基本的cache抽象,方便切换各种底层cache。
- 通过注解Cache可以实现逻辑代码透明缓存。
- 支持事故回滚时也自动回滚缓存。
- 支持复杂的缓存逻辑。
API及默认实现
- Cache接口:主要是缓存的增删查功能。
package org.springframework.cache;
public 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
}
}
由此可见,缓存的对象就是键值对。
默认实现:
- ConCurrentMapCache:使用java.util.concurrentHashMap实现的Cache。
- GuavaCache: 对Gguava com.google.common.cache.Cache进行的Wrapper,需要Google Guava 12.0或更高版本。
- EhCacheCache: 使用Ehcache实现。
JCacheCache:对javax.cache.Cache进行的Wrapper。
CacheManager:Spring提供的缓存管理器,便于管理程序中的多个cache。
package org.springframework.cache;
import java.util.Collection;
public interface CacheManager {
Cache getCache(String name); //根据Cache名字获取Cache
Collection<String> getCacheNames(); //得到所有Cache的名字
}
- 默认实现:
- SimpleCacheManager
- NoOpCacheManager
- ConcurrentMapCacheManager
- CompositeCacheManager
- EhCacheCacheManager
- RedisCacheManager: 来自spring data redis项目
GemfireCacheManager:来自spring data gemfire项目
具体要选择哪个取决于要使用的底层缓存。
另外还提供了CompositeCacheManager用于组合CacheManager,即可以从多个CacheManager轮询得到相应的cache。
<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager"> <!--注入多个CacheManager--> <property name="cacheManagers"> <list> <ref bean="ehcacheManager"/> <ref bean="jcacheManager"/> </list> </property> <property name="fallbackToNoOpCache" value="true"/> </bean>
除了多出cacheManagers属性外,其他方法应该和CacheManager差不多,比如,当调用cacheManager.getCache(cacheName)时,会先从第一CacheManager找起,找到为止,最终找不到的话,因为fallbackToNoOpCache=true,将返回一个NOP的cache,否则返回null。
事务回滚:除了GuavaCacheManager之外,其他Cache都支持Spring事务,即如果事务回滚了,C阿策划的数据也会移除掉。
Spring不进行cache的定义,也不进行Cache的缓存策略的维护。这些都是由底层cache自己实现,然后外部创建一个cache注入进来的。Spring只是提供了一个Wrapper,提供一套对外一致的API。
示例
采用Ehcache,依赖:
<dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>2.6.6</version> </dependency>
配置底层ehcache:
<?xml version="1.0" encoding="UTF-8"?> <ehcache name="es"> <diskStore path="java.io.tmpdir"/> <!--这个好像可有可无--> <defaultCache maxEntriesLocalHeap="1000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="3600" overflowToDisk="false"> </defaultCache> <!--定义一个Cache,名为user,CacheManager维护一个CacheMap,以下name属性为key, cache实例为value,调用getCache("user")即执行cacheMap.get("user"), 返回cache实例--> <cache name="user" maxEntriesLocalHeap="2000" eternal="false" timeToIdleSeconds="600" timeToLiveSeconds="600" overflowToDisk="false" statistics="true"> </cache> <!--可以继续定义多个Cache--> </ehcache>
创建CacheManager:一般Spring的CacheManager的实现都是不会添加setCache()的方法,但会添加setCacheManager(),把其他框架的CacheManager设置进来,而其他框架的CacheManager(比如net.sf.ehcache的CacheManager)则可以设置cache,设置进去之后spring的CacheManager会自动把其中的Cache集合获取到自己维护的CacheMap中。
- 所以创建CacheManager分三步,先做个测试:
@Test
public void test() throws IOException{
//2. 把底层cache设置到底层CacheManager;这里使用net.sf.ehcache的CacheManager
CacheManager ehcacheManager=new CacheManager(
new ClassPathResource("ehcache.xml").getInputStream()); //1. 创建
底层cache
//3. 把底层CacheManager设置到spring的CacheManager
EhCacheCacheManager cacheCacheManager=new EhCacheCacheManager();
cacheCacheManager.setCacheManager(ehcacheManager);
//根据缓存名字获取缓存;使用spring的cache;“user”cache定义在ehcache.xml中
Cache cache=cacheCacheManager.getCache("user");
//往缓存写数据
Long id=1L;
User user=new User(id,"zhang","zhang@gmail.com");
cache.put(id,user); //id为键,user等下被转为json作值
//从缓冲读数据
Assert.assertNotNull(cache.get(id,User.class));
//根据id找值,再根据User类信息转json为类
}
上面三步用配置文件实现更快:
<!--2. 创建底层CacheManager;Spring提供EhCacheManagerFactoryBean来简化 外部依赖ehcache的CacheManager的创建;指定configuration路径比编码方式简单多了--> <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <!--1. 创建底层cache--> <property name="configuration" value="classpath:ehcache.xml"/> </bean> <!--3. 创建spring的CacheManager--> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"> <property name="cacheManager" value="ehcacheManager"/> <!--是否支持事务,是则事务回滚,缓存也回滚,默认false--> <property name="transactionAware" value="true"/> </bean>
上面测试其实就是使用Java configure方式,我们把它写成个配置类来代替上述xml配置:
@Configuration //配置spring容器
@EnableCaching(proxyTargetClass = true)
public class CacheConfig {
@Bean //往容器注册bean实例
public CacheManager cacheManager() {
try {
//2. 创建底层CacheManager
net.sf.ehcache.CacheManager ehcacheCacheManager=
new net.sf.ehcache.CacheManager(
//1. 创建底层cache
new ClassPathResource("ehcache.xml").getInputStream()
);
//3. 创建spring的CacheManager
EhCacheCacheManager cacheCacheManager=
new EhCacheCacheManager(ehcacheCacheManager);
return cacheCacheManager;
} catch (IOException e) { //getInputStream()抛出的
throw new RuntimeException(e);
}
}
}