Spring Cache使用

  • 為什么要用spring cache

平日我們的查詢代碼一般如下:

    public Cat getCatByName(String name) {
        //從緩存中查數(shù)據(jù)
        String key = "Cat:" + name;
        byte[] bytes = redis.getBytes(key);
        if (bytes != null) {
            return protostuffSerializer.deserialize(bytes);
        }
       //如果緩存中找不到數(shù)據(jù),從數(shù)據(jù)中獲取
       String sql = "select * from cat where name = ?";
        List<Cat> cats = jdbcTemplate.query(sql, name, new BeanPropertyRowMapper<>(Cat.class));
       Cat cat = null;
       if(cats != null && cats.size() > 0){
             cat = cats.get(0);
       }
       //設(shè)置緩存
        redis.setex(key, DateUtils.ONE_HOURS_SECOND, protostuffSerializer.serialize(cat));
        return cat;
    }

????寫(xiě)多了代碼,會(huì)發(fā)現(xiàn)查詢的代碼一般都比較固定,即從先緩存中獲取數(shù)據(jù),如果緩存中沒(méi)有數(shù)據(jù)就從數(shù)據(jù)庫(kù)中獲取,然后再設(shè)置緩存.除了查詢代碼,添加或更新數(shù)據(jù)的代碼一般是先更新完數(shù)據(jù)庫(kù)后,再更新緩存;而刪除代碼,一般是刪除完數(shù)據(jù)庫(kù)后,再刪除完緩存.
????有沒(méi)有發(fā)現(xiàn)設(shè)置緩存的代碼一般也比較死,比較一樣.那有沒(méi)有辦法把緩存代碼抽離出來(lái),讓框架幫我們?nèi)プ?我們只需寫(xiě)數(shù)據(jù)庫(kù)的代碼就可以了呢?答案是有的.這就是spring cache.

  • spring cache 可能會(huì)怎么實(shí)現(xiàn)的

說(shuō)到將比較固定的代碼抽離出來(lái),比如日志?我們一般會(huì)想到什么呢?沒(méi)錯(cuò),就是AOP編程.在AOP編程中,緩存的設(shè)置可能會(huì)這么寫(xiě)

    @Around(value = "execution(* com.test.*.*(..)) ")
    public Object calculateTime(ProceedingJoinPoint joinPoint) throws Throwable {
       //從緩存中獲取數(shù)據(jù)
       //獲取參數(shù)
       String key = getArg(point);
       //從redis中獲取值
       String value = redis. get(key);
       if(StringUtils.isNotBlank(value)){
           return value;
       }
       Object object = joinPoint.proceed();
        //設(shè)置緩存
       redis.put(key,object); 
        return object;
    }

????假如讓我們用aop和redis設(shè)計(jì)一個(gè)緩存框架,我們會(huì)怎么做呢?
????對(duì)于查詢操作,我們會(huì)在方法體前面先用redis的get()出緩存,在方法體后面我們會(huì)用redis的put()保存緩存;對(duì)于保存和更新操作,我們會(huì)用redis的del()先刪掉緩存,再用put()把新緩存保存上去;而對(duì)于刪除操作,我們會(huì)在方法體后面用redis的del()刪除掉緩存.
????沒(méi)錯(cuò),spring cacheManager的api也差不多是這么做的.而大名鼎鼎的注解@Cacheable,@CachePut,@CacheEvict分別對(duì)應(yīng)上面三種情況.

  • 注解

1 @Cacheable(value="hashMap",key = "'User:' + #name", condition = "#name.length() > 5")

作用 : 對(duì)查詢操作,當(dāng)調(diào)用加了@Cacheable注解的方法時(shí),jvm會(huì)根據(jù)配置的key去緩存中尋找是否有對(duì)應(yīng)的緩存,如果有就直接返回,如果沒(méi)有才會(huì)繼續(xù)執(zhí)行代碼,當(dāng)執(zhí)行完代碼后,jvm還會(huì)把返回結(jié)果保存到緩存中.
value : 緩存保存的位置, 對(duì)應(yīng)我們?cè)趚ml配置文件中配置的 name = "hashMap" 的緩存 .
key : 緩存都是以鍵值對(duì)的形式保存的,我們可以用#引用一些變量
condition : 條件, #name.length() > 5表示 當(dāng)key的長(zhǎng)度 大于5的時(shí)候緩存注解才生效

2 @CacheEvict(value="hashMap",key = "'User:' + #name",@beforeInvocation=true)

作用 : 對(duì)應(yīng)刪除操作,當(dāng)注解了@CacheEvict的方法被調(diào)用結(jié)束后,key = "'User:' + #name" 的緩存會(huì)被清除掉.
@beforeInvocation=true : 當(dāng)時(shí)如果方法還沒(méi)執(zhí)行完就報(bào)錯(cuò)跳出了,這時(shí)緩存并不會(huì)被清掉,如果我們想在方法執(zhí)行前就清掉緩存的話,我們可以配置一個(gè)beforeInvocation的注解

3 @CachePut(value="hashMap",key = "'User:' + #name")

作用 : 對(duì)應(yīng)保存和更新操作,注解了@CachePut的方法,執(zhí)行前會(huì)把key對(duì)應(yīng)的緩存清掉,執(zhí)行完方法以后會(huì)把結(jié)果保存到key上去.

  • spring cache 的簡(jiǎn)單實(shí)例

xml

   <!-- 啟用緩存注解功能兔魂,這個(gè)是必須的,否則注解不會(huì)生效 -->
    <cache:annotation-driven cache-manager="cacheManager" />

    <!--spring cache 管理器 -->
    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <!--定義了緩存的實(shí)現(xiàn)方式,這里是基于HashMap的-->
                <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" >
                   <!--緩存的名字叫做hashMap,注解里的value對(duì)應(yīng)的值-->
                    <property name="name" value="hashMap"/>
                </bean>
            </set>
        </property>
    </bean>

Java

    /**
     * 我們執(zhí)行兩次該方法,會(huì)發(fā)現(xiàn)只有第一次輸出 first get, 第二次不輸出,說(shuō)明設(shè)置成功
     */
    @Cacheable(value = "hashMap", key = "'User:' + #name")
    public User getUserByName(String name){
        System.out.println("first get");
        User user = new User();
        user.setName("HZP");
        user.setAge(23);
        return user;
    }
  • Spring Cache 整合 Redis

同樣的我們先實(shí)現(xiàn)一個(gè)基于redis的緩存管理器

import com.v56.common.show.utils.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import redis.clients.jedis.PipelineCluster;
import redis.clients.jedis.serializable.ProtostuffSerializer;

public class RedisCache implements Cache {

    private Logger logger = LoggerFactory.getLogger(getClass());
    //redis工具
    private PipelineCluster redis;
    //序列化工具
    private ProtostuffSerializer protostuffSerializer = null;
   //緩存名字
    private String name;

    public void setRedis(PipelineCluster redis) {
        this.redis = redis;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setProtostuffSerializer(ProtostuffSerializer protostuffSerializer) {
        this.protostuffSerializer = protostuffSerializer;
    }

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

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

   //從redis中獲取緩存的操作
    @Override
    public ValueWrapper get(Object key) {
        byte[] bytes = redis.getBytes((String) key);
        return bytes != null ? new SimpleValueWrapper(protostuffSerializer.deserialize(bytes)) : null;
    }

    //把數(shù)據(jù)放到緩存中
    @Override
    public void put(Object key, Object value) {
        redis.setex((String) key, DateUtil.ONE_DAY_SECOND, protostuffSerializer.serialize(value));
    }
    //刪除緩存
    @Override
    public void evict(Object key) {
        redis.del((String) key);
    }
    //清除全部緩存
    @Override
    public void clear() {

    }
}

還要再xml中定義,把我們RedisCache配置進(jìn)去即可,簡(jiǎn)單方便

    <!-- 這里是我們公司自己定義的一套redis-->
    <bean id="redisClusterFactory4fastjdbc" class="com.v56.qf.fastjdbc.web.RedisClusterFactory" init-method="init">
        <property name="appId" value="${fastjdbc.redis.appId}"/>
        <property name="maxTotal" value="${fastjdbc.redis.maxTotal}"/>
        <property name="maxIdle" value="${fastjdbc.redis.maxIdle}"/>
        <property name="minIdle" value="${fastjdbc.redis.minIdle}"/>
        <property name="maxWaitMillis" value="${fastjdbc.redis.maxWaitMillis}"/>
    </bean>
    <bean id="redisCluster4fastjdbc" factory-bean="redisClusterFactory4fastjdbc" factory-method="getRedisCluster"/>
    <bean id="protostuffSerializer4fastjdbc" class="redis.clients.jedis.serializable.ProtostuffSerializer"/>
    <!--REDIS CONFIG For FastJDBC END -->

    <!-- 啟用緩存注解功能翠霍,這個(gè)是必須的儒老,否則注解不會(huì)生效千劈,另外梯码,該注解一定要聲明在spring主配置文件中才會(huì)生效 -->
    <cache:annotation-driven cache-manager="cacheManager" />

    <!-- spring自己的換管理器吆鹤,這里定義了兩個(gè)緩存位置名稱 厨疙,既注解中的value -->
    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
              <!--這里是我們自己寫(xiě)的緩存管理器-->
                <bean class="com.v56.qf.labor2018.cache.RedisCache">
                    <property name="redis" ref="redisCluster4fastjdbc" />
                    <property name="protostuffSerializer" ref = "protostuffSerializer4fastjdbc" />
                    <property name="name" value="redisCache" />
                </bean>
            </set>
        </property>
    </bean>
  • 注意

PS: Spring cache 是基于aop proxy進(jìn)行操作的,在proxy代理中,如果是內(nèi)部調(diào)用,是不會(huì)走代理類的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市疑务,隨后出現(xiàn)的幾起案子沾凄,更是在濱河造成了極大的恐慌,老刑警劉巖知允,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撒蟀,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡温鸽,警方通過(guò)查閱死者的電腦和手機(jī)保屯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)涤垫,“玉大人姑尺,你說(shuō)我怎么就攤上這事◎疴” “怎么了股缸?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)吱雏。 經(jīng)常有香客問(wèn)我敦姻,道長(zhǎng)瘾境,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任镰惦,我火速辦了婚禮迷守,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘旺入。我一直安慰自己兑凿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布茵瘾。 她就那樣靜靜地躺著礼华,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拗秘。 梳的紋絲不亂的頭發(fā)上圣絮,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音雕旨,去河邊找鬼扮匠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛凡涩,可吹牛的內(nèi)容都是我干的棒搜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼活箕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼力麸!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起育韩,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤克蚂,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后座慰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡翠拣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年版仔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片误墓。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蛮粮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谜慌,到底是詐尸還是另有隱情然想,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布欣范,位于F島的核電站变泄,受9級(jí)特大地震影響令哟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜妨蛹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一屏富、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛙卤,春花似錦狠半、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至行嗤,卻和暖如春已日,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背昂验。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工捂敌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人既琴。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓占婉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親甫恩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子逆济,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)磺箕,斷路器奖慌,智...
    卡卡羅2017閱讀 134,656評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,810評(píng)論 6 342
  • 1.pom添加 這里增加spring-data-redis 和jedis 必須要jar包。 2.spring 配置...
    水花一現(xiàn)閱讀 12,544評(píng)論 11 26
  • 本文轉(zhuǎn)自被遺忘的博客 Spring Cache 緩存是實(shí)際工作中非常常用的一種提高性能的方法, 我們會(huì)在許多場(chǎng)景下...
    quiterr閱讀 1,078評(píng)論 0 8
  • (本文的稱呼并沒(méi)有絲毫階級(jí)意識(shí)松靡,請(qǐng)閱讀的小伙伴們摘掉有色眼鏡简僧,偷偷告訴你:我們這個(gè)家特別純潔、美麗) 又是一個(gè)清晨...
    書(shū)寫(xiě)心思閱讀 414評(píng)論 1 6