Spring Boot 整合 RedisCache,EhCache,GuavaCache實戰

1. Spring 的Cache框架

??????整合 不是分別整合幾種緩存,而是同時使用多種緩存。根據項目中不同的緩存需求采用不同的緩存技術。

1.1 一次聊天

??????前些日子和朋友聊天時,他們項目中用到緩存,聊天中了解到,他們的緩存采用的是自己寫的一個叫ICache的接口。緩存有redis緩存 和 OCS緩存(并不知道他們這個是什么鬼)。
看了他們接口API,基本和 spring的cache 框架一樣。
其實他們也可以使用spring cache ,只需要編寫一個OCSCache和 OCSCacheManager就行了。
一般項目中用緩存都是單一使用的,像朋友這樣用好幾種的不太多。其實spring cache 是支持多種緩存混合使用。
下面就是怎么利用spring cache 實現多種緩存技術的混合使用的。

1.2 Cache 和 CacheManager

spring 通過 cache 和 cacheManager 整合 和 管理不同緩存技術,通過 對應的CacheManager 管理 Cache,
比如 redis 、guava、ehcache、Jcache、還有自定義的cache。

  • org.springframework.cache.Cache
  • org.springframework.cache.CacheManager

1.3 緩存注解

??????? Spring Cache 十分強大,在使用緩存時,對項目幾乎沒有侵入,使用時
只需要在需要使用的方法上面加上對應的注解,并且配合強大的SPEL,就可以很簡單的使用各種緩存啦。以后切換緩存也是很方便。注解怎么使用可以參考其他的關于這方便的介紹。

  • @Cacheable 查詢緩存
  • @CacheEvict 刪除緩存條目
  • @CachePut 更新緩存條目
  • @Caching
  • @CacheConfig
  • @EnableCaching 啟用Spring Cache

1.4 CacheMangager

???????CacheManager簡單描述就是用來存放Cache,Cache用于存放具體的key-value值。舉個栗子:一個班級管理員 可以根據名字找到對應的學生,那么cacheManager 也是如此,CacheManager 負責緩存的創建和管理。常見的有下面集中。

  • RedisCacheManager 管理redis緩存
  • GuavaCacheManager 谷歌的guava緩存
  • EhchcheManager EhCache 管理
  • CompositeCacheManager 混合的緩存管理(可以同時使用多種緩存)

2. SpringBoot整合多種緩存

???????
有時候在項目中會用到多種緩存同時使用的情況,就需要通過Spring提供的CompositeCacheManager來整合多種緩沖,通過緩存名字來指定使用的緩存。恕我語言匱乏,實在不知道該怎么說,只能貼代碼了。有耐心的看下代碼,就知道怎么做了。不懂的可以留言問我。

2.1 引入jar

<!-- spring-cache -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- 谷歌的guava cache -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>23.3-jre</version>
</dependency>
<!-- redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>   
<!-- EhCache -->
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>

2.2 配置文件

  • application.properties:
# redis配置
spring.redis.host=localhost 
spring.redis.port=6379
spring.redis.pool.max-idle=8
spring.redis.pool.max-wait=-1
spring.redis.pool.max-active=8
# 指定ehcahce 配置文件路徑
spring.cache.ehcache.config=cache/ehcache.xml
spring.redis.database=0
  • ehcache.xml EhCachede配置文件,其實不想用XML.
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <!-- diskStore:為緩存路徑,ehcache分為內存和磁盤兩級,此屬性定義磁盤的緩存位置。
    參數解釋如下: user.home – 用戶主目錄
    user.dir – 用戶當前工作目錄
    java.io.tmpdir – 默認臨時文件路徑 -->
    <diskStore path="java.io.tmpdir/Tmp_EhCache" />
    <!-- defaultCache:默認緩存策略,當ehcache找不到定義的緩存時,則使用這個緩存策略。只能定義一個。 -->
    <!-- name:緩存名稱。
        maxElementsInMemory:緩存最大數目
        maxElementsOnDisk:硬盤最大緩存個數。
        eternal:對象是否永久有效,一但設置了,timeout將不起作用。
        overflowToDisk:是否保存到磁盤,當系統當機時
        timeToIdleSeconds:設置對象在失效前的允許閑置時間(單位:秒)。僅當eternal=false對象不是永久有效時使 用,可選屬性,默認值是0,也就是可閑置時間無窮大。
        timeToLiveSeconds:設置對象在失效前允許存活時間(單位:秒)。最大時間介于創建時間和失效時間之間。僅 當eternal=false對象不是永久有效時使用,默認是0.,也就是對象存活時間無窮大。
        diskPersistent:是否緩存虛擬機重啟期數據Whether the disk store persists between restarts
        of the Virtual Machine. The default value is false.
        diskSpoolBufferSizeMB:這個參數設置DiskStore(磁盤緩存)的緩存區大小。默認是30MB。每個Cache都應該 有自己的一個緩沖區。
        diskExpiryThreadIntervalSeconds:磁盤失效線程運行時間間隔,默認是120秒。
        memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理內 存。默認策略是LRU(最近最少使用)。
        你可以設置為FIFO(先進先出)或是LFU(較少使用)。
        clearOnFlush:內存數量最大時是否清除。
        memoryStoreEvictionPolicy:可選策略有:LRU(最近最少使用,默認策略)、FIFO(先進先出)、LFU(最少 訪問次數)。
         FIFO,first in first out,這個是大家最熟的,先進先出。
         LFU, Less Frequently Used,就是上面例子中使用的策略,直白一點就是講一直以來最少被使用的。如上面 所講,緩存的元素有一個hit屬性,hit值最小的將會被清出緩存。
         LRU,Least Recently Used,最近最少使用的,緩存的元素有一個時間戳,當緩存容量滿了,而又需要騰出地 方來緩存新的元素的時候,
         那么現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。 -->
    <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
                  timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU" />
    <cache name="EhcacheA" eternal="false" maxElementsInMemory="10000" overflowToDisk="false" diskPersistent="false"
           timeToIdleSeconds="0" timeToLiveSeconds="0" memoryStoreEvictionPolicy="LFU" />
</ehcache>
  • RedisConfig Redis配置 這個很簡潔
package com.example.demo.config;

import org.apache.log4j.Logger;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;


/**
 *redis 配置
 */
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    private static Logger logger = Logger.getLogger(RedisConfig.class);
    @Bean
    @ConfigurationProperties(prefix="spring.redis")
    public JedisPoolConfig getRedisConfig(){
        JedisPoolConfig config = new JedisPoolConfig();
        return config;
    }



    @Bean
    public JedisConnectionFactory getConnectionFactory(RedisProperties redisProperties){
        JedisConnectionFactory factory = new JedisConnectionFactory();
        JedisPoolConfig config = getRedisConfig();
        factory.setDatabase(redisProperties.getDatabase());
        factory.setHostName(redisProperties.getHost());
        factory.setPassword(redisProperties.getPassword());
        factory.setPort(redisProperties.getPort());
        factory.setPoolConfig(config);
        logger.info("JedisConnectionFactory bean init success.");
        return factory;
    }


    @Bean
    public StringRedisTemplate getRedisTemplate( RedisProperties redisProperties){
        JedisConnectionFactory connectionFactory = getConnectionFactory(redisProperties);
        StringRedisTemplate template = new StringRedisTemplate(connectionFactory);
        //設置redis 序列化
        template.setStringSerializer(new StringRedisSerializer());
        return template;
    }

}

  • CacheConfig.java 這個緩存的配置
package com.example.demo.config;

import com.example.demo.constant.CacheConstant;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.cache.guava.GuavaCacheManager;
import org.springframework.cache.support.CompositeCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.cache.DefaultRedisCachePrefix;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author huxingnan
 * @date 2018/4/11 14:05
 */
@Configuration // 自動配置
@EnableCaching //啟用Spring cache
public class CacheConfig {

    // 注釋掉的 是我自己 分別配置幾種緩存的時候用的 spring ioc 中只能有一個 CacheManager 實列,如果 有多個會報錯。    
    //@Bean
//    public CacheManager guavaCacheManager() {
//        GuavaCacheManager cacheManager = new GuavaCacheManager();
//        cacheManager.setCacheBuilder(CacheBuilder.newBuilder().expireAfterWrite(3600, TimeUnit.SECONDS).maximumSize(1000));
//        return cacheManager;
//    }
//
//    @Bean
//    public CacheManager cacheManager(RedisTemplate<Object, Object> redisTemplate) {
//        RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
//        return redisCacheManager;
//    }
    @Value("${spring.cache.ehcache.config}")
    private String ehCacheCongifPath;

    /**
     * @return
     */
    @Bean
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
        EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();
        System.out.println(ehCacheCongifPath);
        cacheManagerFactoryBean.setConfigLocation(new ClassPathResource(ehCacheCongifPath));
        cacheManagerFactoryBean.setShared(true);
        //如果 Factory 自己手動實列化,需要 執行afterPropertiesSet()方法,因為這是方法是 初始化 類使用的
        //如果Factory 由Spring 容器 創建 ,容器初始化完成后 spring 會去執行這個方法。
//        cacheManagerFactoryBean.afterPropertiesSet();//初始化 讀取配置文件,

        return cacheManagerFactoryBean;
    }

    /**
     * 混合緩存管理
     *
     * @param redisTemplate redis template
     * @return cacheManager
     */
    @Bean
    public CacheManager compositeCacheManager(@Autowired RedisTemplate<Object, Object> redisTemplate, @Autowired EhCacheManagerFactoryBean factoryBean) {
        RedisCacheManager redisCacheManager = getRedisCacheManager(redisTemplate);
        GuavaCacheManager guavaCacheManager = getGuavaCacheManager();
        EhCacheCacheManager ehCacheCacheManager = ehCacheCacheManager(factoryBean);
        CompositeCacheManager cacheManager = new CompositeCacheManager(redisCacheManager, guavaCacheManager, ehCacheCacheManager);
        cacheManager.setFallbackToNoOpCache(true);
        cacheManager.afterPropertiesSet();
        return cacheManager;
    }

    /**
     * 獲取guava 實列的緩存
     *
     * @return guava緩存管理 實列
     */
    private GuavaCacheManager getGuavaCacheManager() {
        GuavaCacheManager guavaCacheManager = new GuavaCacheManager();
        guavaCacheManager.setCacheBuilder(CacheBuilder.newBuilder().expireAfterWrite(3600, TimeUnit.SECONDS).maximumSize(1000));
        ArrayList<String> guavaCacheNames = Lists.newArrayList();
        guavaCacheNames.add(CacheConstant.GUAVA_CACHE_A);
        guavaCacheManager.setCacheNames(guavaCacheNames);
        return guavaCacheManager;
    }

    /**
     * 獲取redisCacheManager
     *
     * @param redisTemplate redisTemplate
     * @return redisCacheManager
     */
    private RedisCacheManager getRedisCacheManager(RedisTemplate<Object, Object> redisTemplate) {
        RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
        List<String> redisCacheNames = Lists.newArrayList();
        redisCacheNames.add(CacheConstant.REDIS_CACHE_A);//一個cacheName 對應一個 緩存實列
        redisCacheNames.add(CacheConstant.REDIS_CACHE_B);
        redisCacheManager.setCacheNames(redisCacheNames);
        //redis key 前綴
        redisCacheManager.setCachePrefix(new DefaultRedisCachePrefix("demo"));//緩存key 前綴
        redisCacheManager.setUsePrefix(true);//使用前綴
        redisCacheManager.initializeCaches();//rediscache 需要初始化 緩存
        return redisCacheManager;
    }


    /**
     * EhCacheManager
     *
     * @return EhCacheManager
     */
    private EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean factoryBean) {
        EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager(factoryBean.getObject());
        //由于自己實列化EhCacheManager 需要執行 手動初始化 方法。
        ehCacheCacheManager.initializeCaches();//初始化
        return ehCacheCacheManager;
    }


}

2.3 怎么用

  • 測試對象Account
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {

    private String id;
    private String userName;
    private String passWord;
    @DateTimeFormat(pattern = "yyy-MM-dd")
    private Date createTime;
    private String alias;
    private Integer level;
    private Boolean vip;
}

  • AccountService
/**
 * @author huxingnan
 * @date 2018/4/12 13:17
 */
@Service
public class AccountServiceImpl implements AccountService {
    @Override
    @CachePut(value = CacheConstant.EHCACHE_A,key = "#account.id")
    public Account saveAccount(Account account) {

        System.out.println("保存成功"+account);
        account.setId("999");
        account.setCreateTime(new Date());
        return account;
    }

    @Override
    @Cacheable(value = CacheConstant.EHCACHE_A,key = "#account.id")
    public Account getAccountById(Account account) {
        Account account1 = new Account(account.getId(),"zhangfei","zf12345",new Date(),"張飛",2,false);
        return account1;
    }

    @Override
    @Cacheable(value = CacheConstant.EHCACHE_A,key="#account.userName")
    public List<Account> getAccountList(Account account) {
        List<Account> accountList = Lists.newArrayList();
        accountList.add(new Account("124","zhaoyun","zy9999",new Date(),"趙云",3,true));
        accountList.add(new Account("125","lvbu","zy9999",new Date(),"呂布",3,true));
        System.out.println("查詢accountList"+account);
        return accountList;
    }

    @Override
    @CacheEvict(value = CacheConstant.EHCACHE_A,key="#account.id")
    public int deleteAccountById(Account account) {
        System.out.println("刪除account"+account);
        return 1;
    }

    @Override
    @CacheEvict(value = CacheConstant.EHCACHE_A,key = "#p0.id")
    public int updateAccountById(Account account) {
        System.out.println("更新account"+account);
        return 1;
    }
}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容