欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

spring的Cache注解和redis的區(qū)別說(shuō)明

 更新時(shí)間:2021年12月31日 10:52:08   作者:吧唧小豬  
這篇文章主要介紹了spring的Cache注解和redis的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

spring Cache注解和redis區(qū)別

1.不支持TTL

即不能設(shè)置過(guò)期時(shí)間 expires time,SpringCache 認(rèn)為這是各個(gè)Cache實(shí)現(xiàn)自己去完成的事情,有方案但是只能設(shè)置統(tǒng)一的過(guò)期時(shí)間,明顯不夠靈活。

2.內(nèi)部調(diào)用

非 public 方法上使用注解,會(huì)導(dǎo)致緩存無(wú)效。內(nèi)部調(diào)用方法的時(shí)候不會(huì)調(diào)用cache方法。

由于 SpringCache 是基于 Spring AOP 的動(dòng)態(tài)代理實(shí)現(xiàn),由于代理本身的問(wèn)題,當(dāng)同一個(gè)類(lèi)中調(diào)用另一個(gè)方法,會(huì)導(dǎo)致另一個(gè)方法的緩存不能使用,這個(gè)在編碼上需要注意,避免在同一個(gè)類(lèi)中這樣調(diào)用。如果非要這樣做,可以通過(guò)再次代理調(diào)用,如 ((Category)AopContext.currentProxy()).get(category) 這樣避免緩存無(wú)效。

3.key的問(wèn)題

在清除緩存的時(shí)候,無(wú)法指定多個(gè)緩存塊,同時(shí)清除多個(gè)緩存的key。

Spring Cache注解+redis整合及遇到的坑

背景:項(xiàng)目經(jīng)理讓我做緩存,我心想不是有redis嗎?他說(shuō)你把Spring Cache注解結(jié)合進(jìn)來(lái),我只好硬著頭皮來(lái)做。

先介紹Spring Cache注解

從3.1開(kāi)始,Spring引入了對(duì)Cache的支持。其使用方法和原理都類(lèi)似于Spring對(duì)事務(wù)管理的支持。Spring Cache是作用在方法上的,其核心思想是這樣的:當(dāng)我們?cè)谡{(diào)用一個(gè)緩存方法時(shí)會(huì)把該方法參數(shù)和返回結(jié)果作為一個(gè)鍵值對(duì)存放在緩存中,等到下次利用同樣的參數(shù)來(lái)調(diào)用該方法時(shí)將不再執(zhí)行該方法,而是直接從緩存中獲取結(jié)果進(jìn)行返回。所以在使用Spring Cache的時(shí)候我們要保證我們緩存的方法對(duì)于相同的方法參數(shù)要有相同的返回結(jié)果。

Spring為我們提供了幾個(gè)注解來(lái)支持Spring Cache。其核心主要是@Cacheable和@CacheEvict。使用@Cacheable標(biāo)記的方法在執(zhí)行后Spring Cache將緩存其返回結(jié)果,而使用@CacheEvict標(biāo)記的方法會(huì)在方法執(zhí)行前或者執(zhí)行后移除Spring Cache中的某些元素。下面我們將來(lái)詳細(xì)介紹一下Spring基于注解對(duì)Cache的支持所提供的幾個(gè)注解。

根據(jù)項(xiàng)目情況只用到@Cacheable,這里面一共有幾個(gè)常用的參數(shù),一個(gè)是value,這一定必須指定其表示當(dāng)前方法的返回值是會(huì)被緩存在哪個(gè)Cache上的,對(duì)應(yīng)Cache的名稱(chēng)。其可以是一個(gè)Cache也可以是多個(gè)Cache,當(dāng)需要指定多個(gè)Cache時(shí)其是一個(gè)數(shù)組。

還有就是key,key屬性是用來(lái)指定Spring緩存方法的返回結(jié)果時(shí)對(duì)應(yīng)的key的。該屬性支持SpringEL表達(dá)式。當(dāng)我們沒(méi)有指定該屬性時(shí),Spring將使用默認(rèn)策略生成key。

我們這里先來(lái)看看自定義策略,至于默認(rèn)策略會(huì)在后文單獨(dú)介紹自定義策略是指我們可以通過(guò)Spring的EL表達(dá)式來(lái)指定我們的key。

這里的EL表達(dá)式可以使用方法參數(shù)及它們對(duì)應(yīng)的屬性。使用方法參數(shù)時(shí)我們可以直接使用“#參數(shù)名”或者“#p參數(shù)index”。最后一個(gè)就是condition,有的時(shí)候我們可能并不希望緩存一個(gè)方法所有的返回結(jié)果。通過(guò)condition屬性可以實(shí)現(xiàn)這一功能。condition屬性默認(rèn)為空,表示將緩存所有的調(diào)用情形。

其值是通過(guò)SpringEL表達(dá)式來(lái)指定的,當(dāng)為true時(shí)表示進(jìn)行緩存處理;當(dāng)為false時(shí)表示不進(jìn)行緩存處理,即每次調(diào)用該方法時(shí)該方法都會(huì)執(zhí)行一次。還有幾個(gè)別的注解@CacheEvict,@CachePut,基本跟@Cacheable一樣只是作用不一樣換湯不換藥。

當(dāng)你看到這的時(shí)候我相信你,你已經(jīng)對(duì)Spring緩存注解了解的很多了。下面就來(lái)配置Spring注解與Redis來(lái)整合。(本人不做配置Redis)

配置Spring注解與Redis整合

先導(dǎo)入jar包

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.6.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.3.2</version>
</dependency>

我們?cè)赟pring-config.xml中來(lái)配置

xmlns:cache="http://www.springframework.org/schema/cache"
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-4.2.xsd"
<cache:annotation-driven cache-manager="cacheManager" /> //這句話一定要加上,要不注解不管用
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
    <property name="caches">
        <set>
            <bean class="com.zswy.mkedu.utils.RedisCache"> 
                <property name="redisTemplate" ref="redisTemplate" />
                <!--name一定要與你注解那個(gè)方法中value一樣,否則找不到-->
                <property name="name" value="courseCache" />
            </bean>
        </set>
    </property>
</bean>

來(lái)寫(xiě)RedisCache這個(gè)類(lèi),讓其實(shí)現(xiàn)Cache接口

public class RedisCache implements Cache{ 
    private RedisTemplate<String, Object> redisTemplate;
    private String name;
    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }
 
    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getName() {
        return this.name;
    }
 
    public Object getNativeCache() {
        return this.redisTemplate;
    }
 
    public ValueWrapper get(Object key) {
        final String keyf = (String) key;
        Object object = null;
        object = redisTemplate.execute(new RedisCallback<Object>() {
            public Object doInRedis(RedisConnection connection)
                    throws DataAccessException {
 
                byte[] key = keyf.getBytes();
                byte[] t = name.getBytes();
                byte[] value = connection.hGet(t, key);
                if (value == null) {
                    return null;
                }
                return toObject(value);
            }
        });
        ValueWrapper obj=(object != null ? new SimpleValueWrapper(object) : null);
        return obj;
    }
 
    public <T> T get(Object o, Class<T> aClass) {
        return null;
    }
 
    public void put(Object key, Object value) {
        final String keyf = (String) key;
        final Object valuef = value;
        final long liveTime = 86400;
        redisTemplate.execute(new RedisCallback<Long>() {
            public Long doInRedis(RedisConnection connection)
                    throws DataAccessException {
                byte[] keyb = keyf.getBytes();
                byte[] valueb = toByteArray(valuef);
                byte[] t = name.getBytes();
                connection.hSet(t, keyb, valueb);
                if (liveTime > 0) {
                    connection.expire(keyb, liveTime);
                }
                return 1L;
            }
        });
    }
 
    private byte[] toByteArray(Object obj) {
        byte[] bytes = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            oos.flush();
            bytes = bos.toByteArray();
            oos.close();
            bos.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return bytes;
    }
 
    private Object toObject(byte[] bytes) {
        Object obj = null;
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bis);
            obj = ois.readObject();
            ois.close();
            bis.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
        return obj;
    }
 
    public ValueWrapper putIfAbsent(Object o, Object o1) {
        return null;
    }
 
    public void evict(Object key) {
        final String keyf = (String) key;
        redisTemplate.execute(new RedisCallback<Long>() {
            public Long doInRedis(RedisConnection connection)
                    throws DataAccessException {
                return connection.hDel(name.getBytes(), keyf.getBytes());
            }
        });
    }
 
    public void clear() {
        // 清楚緩存,需要根據(jù)Cache的name屬性,在redis中模糊查詢(xún)相關(guān)key值的集合,并全部刪除
        redisTemplate.execute(new RedisCallback<String>() {
            public String doInRedis(RedisConnection connection)
                    throws DataAccessException {
                byte[] t = name.getBytes();
                Set<byte[]> fields = connection.hKeys(t);
                for (byte[] bs : fields) {
                    connection.hDel(t, bs);
                }
                return "ok";
            }
        });
    }
}

開(kāi)始使用注解,我是把其作用在倒dao層中

注意:這有一個(gè)坑:一般我們都是三層架構(gòu),service層調(diào)dao層,我一開(kāi)始把注解加到Service實(shí)現(xiàn)層,運(yùn)行的時(shí)候Cache機(jī)制不觸發(fā)。我開(kāi)始大量百度,查書(shū),最后發(fā)現(xiàn)根本不會(huì)觸發(fā),因?yàn)镾pring把實(shí)現(xiàn)類(lèi)裝載成為Bean的時(shí)候,會(huì)用代理包裝一下,所以從Spring Bean的角度看,只有接口里面的方法是可見(jiàn)的,其它的都隱藏了,自然課看不到實(shí)現(xiàn)類(lèi)里面的非接口方法,最后我把注解放到dao層上

這里的key我使用的方法名,一般會(huì)使用參數(shù)的屬性,value就是上文在配置文件里配置的name。這樣所有的配置都配好了

運(yùn)行結(jié)果

這個(gè)就是我們存的key

第二個(gè)坑就是,你的實(shí)體類(lèi)一定要序列化 實(shí)現(xiàn)Serializable,否則就報(bào)空指針異常。這個(gè)一定要記住

key的生成策略

鍵的生成策略有兩種,一種是默認(rèn)策略,一種是自定義策略。

默認(rèn)策略:

默認(rèn)的key是通過(guò)KeyGenerator生成的,其默認(rèn)策略如下:

1.如果方法沒(méi)有參數(shù),則使用0作為key;

2.如果只有一個(gè)參數(shù)的話則使用該參數(shù)作為key;

3.如果參數(shù)多于一個(gè)則使用所有參數(shù)的hashcode作為key;

自定義策略:

自定義策略是指我們通過(guò)Spring的EL表達(dá)式來(lái)指定我們的key。這里的EL表達(dá)式可以使用參數(shù)以及它們對(duì)應(yīng)的屬性。使用方法參數(shù)時(shí)我們可以直接使用“#參數(shù)名”或者“#p參數(shù)index”。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • java 使用正則表達(dá)式去除前后空格

    java 使用正則表達(dá)式去除前后空格

    這篇文章主要介紹了java 使用正則表達(dá)式去除前后空格,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • Spring注解@Profile實(shí)現(xiàn)開(kāi)發(fā)環(huán)境/測(cè)試環(huán)境/生產(chǎn)環(huán)境的切換

    Spring注解@Profile實(shí)現(xiàn)開(kāi)發(fā)環(huán)境/測(cè)試環(huán)境/生產(chǎn)環(huán)境的切換

    在進(jìn)行軟件開(kāi)發(fā)過(guò)程中,一般會(huì)將項(xiàng)目分為開(kāi)發(fā)環(huán)境,測(cè)試環(huán)境,生產(chǎn)環(huán)境。本文主要介紹了Spring如何通過(guò)注解@Profile實(shí)現(xiàn)開(kāi)發(fā)環(huán)境、測(cè)試環(huán)境、生產(chǎn)環(huán)境的切換,需要的可以參考一下
    2023-04-04
  • Spring Cloud Ribbon配置詳解

    Spring Cloud Ribbon配置詳解

    這篇文章主要介紹了Spring Cloud Ribbon配置詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Spring Boot 與 Kotlin 使用JdbcTemplate連接MySQL數(shù)據(jù)庫(kù)的方法

    Spring Boot 與 Kotlin 使用JdbcTemplate連接MySQL數(shù)據(jù)庫(kù)的方法

    本文介紹在Spring Boot基礎(chǔ)下配置數(shù)據(jù)源和通過(guò) JdbcTemplate 編寫(xiě)數(shù)據(jù)訪問(wèn)的示例。感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧
    2018-01-01
  • Java創(chuàng)建多線程異步執(zhí)行實(shí)現(xiàn)代碼解析

    Java創(chuàng)建多線程異步執(zhí)行實(shí)現(xiàn)代碼解析

    這篇文章主要介紹了Java創(chuàng)建多線程異步執(zhí)行實(shí)現(xiàn)代碼解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • 詳解Java字符型常量和字符串常量的區(qū)別

    詳解Java字符型常量和字符串常量的區(qū)別

    Java 中的字符型常量和字符串常量是兩種不同的數(shù)據(jù)類(lèi)型,本文將給大家詳細(xì)介紹一下Java字符型常量和字符串常量的區(qū)別,文中通過(guò)代碼講解的非常詳細(xì),需要的朋友可以參考下
    2023-10-10
  • Java判斷上傳圖片格式的實(shí)例代碼

    Java判斷上傳圖片格式的實(shí)例代碼

    這篇文章主要介紹了Java判斷上傳圖片格式的實(shí)例代碼,在文中給大家提到了Java使用文件頭的類(lèi)型判斷上傳的文件是否為圖片類(lèi)型,需要的朋友可以參考下
    2019-06-06
  • Java多線程編程之訪問(wèn)共享對(duì)象和數(shù)據(jù)的方法

    Java多線程編程之訪問(wèn)共享對(duì)象和數(shù)據(jù)的方法

    這篇文章主要介紹了Java多線程編程之訪問(wèn)共享對(duì)象和數(shù)據(jù)的方法,多個(gè)線程訪問(wèn)共享對(duì)象和數(shù)據(jù)的方式有兩種情況,本文分別給出代碼實(shí)例,需要的朋友可以參考下
    2015-05-05
  • springboot自帶的緩存@EnableCaching用法

    springboot自帶的緩存@EnableCaching用法

    這篇文章主要介紹了springboot自帶的緩存@EnableCaching用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • Java實(shí)現(xiàn)雪花算法的原理

    Java實(shí)現(xiàn)雪花算法的原理

    這篇文章主要介紹了Java實(shí)現(xiàn)雪花算法的原理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05

最新評(píng)論