guava cache與spring集成

緩存的背景

    緩存,在咱們平常開發中是必不可少的一種解決性能問題的方法。簡單的說,cache 就是爲了提高系統性能而開闢的一塊內存空間。在cpu進行計算的時候, 首先是讀取寄存器,而後內存,再是硬盤。因爲寄存器容量很小,不太適合存儲咱們須要快速讀取的數據,放在硬盤中話,效率過低,因此大多數人將一些靜態資源或者不常常修改的數據放在內存中。  java

緩存的做用

    緩存的主要做用是暫時在內存中保存業務系統的數據處理結果,而且等待下次訪問使用。在平常開發的不少場合,因爲受限於硬盤 IO的性能或者咱們自身業務系統的數據處理和獲取可能很是費時,當咱們發現咱們的系統這個數據請求量很大的時候,頻繁的IO和頻繁的 邏輯處理會致使硬盤和CPU資源的瓶頸出現。緩存的做用就是將這些來自不易的數據保存在內存中,當有其餘線程或者客戶端須要查詢相同 的數據資源時,直接從緩存的內存塊中返回數據,這樣不但能夠提升系統的響應時間,同時也能夠節省對這些數據的處理流程的資源消耗,總體上來講,系統性能會有大大的提高。redis

guava cache與spring整合

    你們都清楚,相對於靜態Map實現本地緩存而言guava cache提供了許多種對緩存管理策略,好比:緩存個數、緩存生存期、緩存提取策略(LRU)等, 也正是這樣,guava cache在本地緩存層面上使用是開發人員的首選。而在企業開發中spring是用的最多的,若是將guava cache與spring整合,依靠spring強大的IOC和AOP在使用緩存時是很方便、 快捷的,而要實現spring與guava cache整合 只須要如下三個步驟就能夠搞定:spring

創建spring-cache.xml

    首先創建spring-cache.xml 裏面配置spring對guava cache基本參數的管理,以下:緩存

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.0.xsd">

    <!--啓動註解  進行guava cache 管理-->
    <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>
    <bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
        <property name="cacheManagers">
            <list>
	    <!--將guava cache交於spring管理  若是要實現redis  也能夠加在這裏-->
                <ref bean="guavaCacheManager"/>
            </list>
        </property>
        <property name="fallbackToNoOpCache" value="true"/>
    </bean>

 <!--配置guava cache須要緩存的key  以及創建方式-->
    <bean id="guavaCacheManager" class="com.daojia.open.confluence.worker.common.GuavaCacheManager">
        <property name="configMap">
            <map key-type="java.lang.String" value-type="com.google.common.cache.CacheBuilder">
                <entry key="indexReptileKeyValue" value-ref="defaultCacheBuilder"/>
            </map>
        </property>
    </bean>
<!--設置guava cache緩存策略  生存期爲24小時  最大100個緩存數-->
    <bean id="defaultCacheBuilder"
          class="com.google.common.cache.CacheBuilder"
          factory-method="from">
        <constructor-arg value="maximumSize=100, expireAfterWrite=24h"/>
    </bean>
</beans>

實現spring對外提供的接口

    上面配置了將guavaCacheManager交於spring管理,那麼久要實現這個管理類,該類若是要被spring容器管理的話須要 實現AbstractTransactionSupportingCacheManager接口,該接口支持事物回滾,當緩存失敗,會將錯誤數據還原。具體實現以下:app

package com.daojia.open.confluence.worker.common;

import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Maps;
import org.springframework.cache.Cache;
import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;

/**
 * guavacache支持事務回滾,重寫manager。
 * cache的構建使用builder配置構建
 */
public class GuavaCacheManager  extends AbstractTransactionSupportingCacheManager {

    private final ConcurrentMap<String, Cache> cacheMap = Maps.newConcurrentMap();

    private boolean dynamic = true;

    private Map<String, CacheBuilder> builderMap = Maps.newHashMap();

    private boolean allowNullValues = true;

    @Override
    protected Collection<? extends Cache> loadCaches() {
        Collection<Cache> values = cacheMap.values();
        return values;
    }

    @Override
    public Cache getCache(String name) {
        Cache cache = this.cacheMap.get(name);
        if (cache == null && this.dynamic) {
            synchronized (this.cacheMap) {
                cache = this.cacheMap.get(name);
                if (cache == null && this.builderMap.containsKey(name)) {
                    CacheBuilder builder = this.builderMap.get(name);
                    cache = createGuavaCache(name, builder);
                    this.cacheMap.put(name, cache);
                }
            }
        }
        return cache;
    }

    protected Cache createGuavaCache(String name, CacheBuilder builder) {
        com.google.common.cache.Cache<Object, Object> cache = null;
        if(builder == null){
            cache = CacheBuilder.newBuilder().build();
        }else{
            cache = builder.build();
        }
        return new GuavaCache(name, cache, isAllowNullValues());
    }


    public boolean isAllowNullValues() {
        return this.allowNullValues;
    }

    public void setConfigMap(Map<String, CacheBuilder> configMap) {
        this.builderMap = configMap;
    }

}

    這僅僅是對guava cache管理 還須要一個具體的緩存操做類,該類也是同樣要被spring管理,要實現一個cache接口,該接口是spring對外緩存管理的統一接口,只要實現該 接口的緩存,都能被spring管理起來,好比guava cache 以下:ide

package com.daojia.open.confluence.worker.common;

import com.daojia.open.confluence.worker.utils.JsonMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

public class GuavaCache implements Cache {

    private static Logger log = LoggerFactory.getLogger(GuavaCache.class);

    private final JsonMapper mapper = new JsonMapper();

    private static final Object NULL_HOLDER = new NullHolder();

    private final String name;

    private final com.google.common.cache.Cache<Object, Object> cache;

    private final boolean allowNullValues;

    public GuavaCache(String name, com.google.common.cache.Cache<Object, Object> cache) {
        this(name, cache, true);
    }

    public GuavaCache(String name, com.google.common.cache.Cache<Object, Object> cache, boolean allowNullValues) {
        Assert.notNull(name, "Name must not be null");
        Assert.notNull(cache, "Cache must not be null");
        this.name = name;
        this.cache = cache;
        this.allowNullValues = allowNullValues;
    }



    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public Object getNativeCache() {
        return this.cache;
    }

    @Override
    public ValueWrapper get(Object key) {
        key = getKey(key.toString());
        Object value = this.cache.getIfPresent(key);
        log.info("獲取緩存key={},獲取對象={}", key, mapper.toJson(value));
        return toWrapper(value);
    }

    @Override
    public <T> T get(Object key, Class<T> type) {
        key = getKey(key.toString());
        Object value = fromStoreValue(this.cache.getIfPresent(key));
        log.info("獲取緩存key={},獲取對象={}", key, mapper.toJson(value));
        if (value != null && type != null && !type.isInstance(value)) {
            throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value);
        }
        return (T) value;
    }

    @Override
    public void put(Object key, Object value) {
        key = getKey(key.toString());
        this.cache.put(key, toStoreValue(value));
        log.info("存入緩存key=={},存入對象={}", key, mapper.toJson(value));
    }

    @Override
    public ValueWrapper putIfAbsent(Object key, Object value) {
        try {
            PutIfAbsentCallable callable = new PutIfAbsentCallable(value);
            Object result = this.cache.get(key, callable);
            return (callable.called ? null : toWrapper(result));
        }
        catch (ExecutionException ex) {
            throw new IllegalStateException(ex);
        }
    }

    @Override
    public void evict(Object key) {
        this.cache.invalidate(key);
        log.info("刪除緩存key={}",key);
    }

    @Override
    public void clear() {
        this.cache.invalidateAll();
        log.info("清空guavacache全部緩存");
    }

    private ValueWrapper toWrapper(Object value) {
        return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null);
    }


    protected Object fromStoreValue(Object storeValue) {
        if (this.allowNullValues && storeValue == NULL_HOLDER) {
            return null;
        }
        return storeValue;
    }

    public final boolean isAllowNullValues() {
        return this.allowNullValues;
    }

    @SuppressWarnings("serial")
    private static class NullHolder implements Serializable {
    }

    protected Object toStoreValue(Object userValue) {
        if (this.allowNullValues && userValue == null) {
            return NULL_HOLDER;
        }
        return userValue;
    }

    private class PutIfAbsentCallable implements Callable<Object> {

        private final Object value;

        private boolean called;

        public PutIfAbsentCallable(Object value) {
            this.value = value;
        }

        @Override
        public Object call() throws Exception {
            this.called = true;
            return toStoreValue(this.value);
        }
    }

    /**實現key增長cache名稱**/
    private String getKey(String key) {
        return name + "_" + key;
    }
}

編寫sqel語法 對緩存進行管理

    當完成這上面兩個步驟以後,代表你的guava cache已被spring管理起來了,這時能夠熟悉一下sqel註釋語法,這是spring 對緩存進行註解管理用到經常使用語法,好比獲取緩存:性能

/*當使用到該方法的時候  spring會首先判斷緩存中是否含有該key的值 ,若是有則從本地緩存中獲取
  並以你輸入的參數indexReptileKey爲key獲取,不然從執行方法體中的獲取  並將獲取的數據放入到緩存中*/
    @Cacheable(value = "indexReptileKeyValue", key = "#indexReptileKey")
    public List<IndexReptilePJO> getReptile(int indexReptileKey) throws Exception {
        LOGGER.info("不走guava cache獲取參數");
        return super.handlerindexReptile();
    }

    放入緩存以下:ui

/*設置某個值的時候,想將該值存入緩存  則用這個註解  將你須要設置的值設置到緩存中 
  另外一個方法在獲取的時候就是從緩存中獲取*/
   @CachePut(value = "indexReptileKeyValue", key = "#indexReptileKey")
    public List<IndexReptilePJO> setReptile(int indexReptileKey) throws Exception {
        LOGGER.info("將樹節點項目路徑  存入guava cache中");
        return super.handlerindexReptile();
    }
相關文章
相關標籤/搜索