简介

  • 自Spring 3.1起,提供了基于注解的Cache支持(之前是用aop实现的),且提供了Cache抽象。
  • Spring Cache优点:
  1. 提供基本的cache抽象,方便切换各种底层cache。
  2. 通过注解Cache可以实现逻辑代码透明缓存。
  3. 支持事故回滚时也自动回滚缓存。
  4. 支持复杂的缓存逻辑。

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);
        }
    }
}