SpringBoot技術(shù)專題-整合SpringCache和Redis

Spring基于注解的緩存

對(duì)于緩存聲明史汗,spring的緩存提供了一組java注解:

@Cacheable:觸發(fā)緩存寫入。

@CacheEvict:觸發(fā)緩存清除怕吴。

@CachePut:更新緩存(不會(huì)影響到方法的運(yùn)行)。

@Caching:重新組合要應(yīng)用于方法的多個(gè)緩存操作县踢。

@CacheConfig:設(shè)置類級(jí)別上共享的一些常見緩存設(shè)置转绷。

@Cacheable注解

顧名思義,@Cacheable可以用來進(jìn)行緩存的寫入硼啤,將結(jié)果存儲(chǔ)在緩存中议经,以便于在后續(xù)調(diào)用的時(shí)候可以直接返回緩存中的值,而不必再執(zhí)行實(shí)際的方法谴返。

  最簡單的使用方式,注解名稱 = 緩存名稱,使用例子如下:

@Cacheable("books")

public Book findBook(ISBN isbn) {...}

一個(gè)方法可以對(duì)應(yīng)兩個(gè)緩存名稱煞肾,如下:

@Cacheable({"books", "isbns"})

public Book findBook(ISBN isbn) {...}

   @Cacheable的緩存名稱是可以配置動(dòng)態(tài)參數(shù)的,比如選擇傳入的參數(shù),如下: (以下示例是使用SpEL聲明,如果您不熟悉

SpEL嗓袱,可以閱讀Spring Expression Language)

@Cacheable(cacheNames="books", key="#isbn")

public Book findBook(ISBN isbn,boolean checkWarehouse,boolean includeUsed)

@Cacheable(cacheNames="books", key="#isbn.rawNumber")

public Book findBook(ISBN isbn,boolean checkWarehouse,boolean includeUsed)

@Cacheable(cacheNames="books", key="T(someType).hash(#isbn)")

public Book findBook(ISBN isbn,boolean checkWarehouse,boolean includeUsed)

@Cacheable還可以設(shè)置根據(jù)條件判斷是否需要緩存

condition:取決于給定的參數(shù)是否滿足條件

unless:取決于返回值是否滿足條件

以下是一個(gè)簡單的例子:

@Cacheable(cacheNames="book", condition="#name.length() < 32")

public Book findBook(String name)

@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result.hardback")

public Book findBook(String name)

@Cacheable還可以設(shè)置:keyGenerator(指定key自動(dòng)生成方法),cacheManager(指定使用的緩存管

理),cacheResolver(指****定使用緩存的解析器)等,這些參數(shù)比較適合全局設(shè)置籍救,這里就不多做介紹了。

@CachePut注解

  @CachePut:當(dāng)需要更新緩存而不干擾方法的運(yùn)行時(shí) 渠抹,可以使用該注解蝙昙。也就是說,始終執(zhí)行該方法梧却,并將結(jié)果放入緩

存,注解參數(shù)與@Cacheable相同。 以下是一個(gè)簡單的例子:

@CachePut (cacheNames="book", key="#isbn")

public Book updateBook(ISBN isbn, BookDescriptor descriptor)

   **通常強(qiáng)烈建議不要對(duì)同一方法同時(shí)使用@CachePut和@Cacheable注解览爵,因?yàn)樗鼈兙哂胁煌男袨槿铝俊?赡軙?huì)產(chǎn)生不可**

思議的BUG哦。

@CacheEvict注解

   @CacheEvict:刪除緩存的注解,這對(duì)刪除舊的數(shù)據(jù)和無用的數(shù)據(jù)是非常有用的荆几。這里還多了一個(gè)參數(shù)(allEntries),設(shè)置

allEntries=true時(shí)吓妆,可以對(duì)整個(gè)條目進(jìn)行批量刪除。 以下是個(gè)簡單的例子:

@CacheEvict(cacheNames="books")

public void loadBooks(InputStream batch)

//對(duì)cacheNames進(jìn)行批量刪除@CacheEvict(cacheNames="books", allEntries=true)

public void loadBooks(InputStream batch)

@Caching注解

   @Caching:在使用緩存的時(shí)候伴郁,有可能會(huì)同時(shí)進(jìn)行更新和刪除耿战,會(huì)出現(xiàn)同時(shí)使用多個(gè)注解的情況.而@Caching可以實(shí)

現(xiàn)。以下是個(gè)簡單的例子:

@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })

public Book importBooks(String deposit, Date date)

@CacheConfig注解

   @CacheConfig:緩存提供了許多的注解選項(xiàng)焊傅,但是有一些公用的操作剂陡,我們可以使用@CacheConfig在類上進(jìn)行全局設(shè)置。 以下是個(gè)簡單的例子:

@CacheConfig("books")

public class BookRepositoryImpl implements BookRepository {

@Cacheable

public Book findBook(ISBN isbn) {...}

}

可以共享緩存名稱狐胎,統(tǒng)一配置KeyGenerator,CacheManager,CacheResolver鸭栖。

實(shí)例

來看看我們在springboot中怎么使用redis來作為緩存吧.

為spring cache配置redis作為緩存

1.在pom.xml引入redis依賴

<dependency>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

2. springboot集成redis配置文件(在本地啟動(dòng)的redis)

在springboot中使用redis,只要配置文件寫有redis配置握巢,代碼就可以直接使用了晕鹊。

spring:

redis:

database: 0 # Database index used by the connection factory.

url: redis://user:@127.0.0.1:6379

Connection URL. Overrides host, port, and password. User is ignored. Example: redis://user:password@example.com:6379host: 127.0.0.1 # Redis server host.

password: # Login password of the redis server.

port: 6379 # Redis server port.

ssl: false # Whether to enable SSL support.

timeout: 5000 # Connection timeout.

3.redis緩存配置類CacheConfig

這里對(duì)spring的緩存進(jìn)行了配置,包括

KeyGenerator,CacheResolver,CacheErrorHandler,CacheManager,還有redis序列化方式暴浦。

/**

  • @author wwj

*/

@Configuration

public class CacheConfig extends CachingConfigurerSupport {

    @Resource

    private RedisConnectionFactory factory;

/**    

* 自定義生成redis-key

*

* @return

*/    

@Override

@Bean

public KeyGenerator keyGenerator() {

    return(o, method, objects) -> {

        StringBuilder sb =new StringBuilder();

        sb.append(o.getClass().getName()).append(".");

        sb.append(method.getName()).append(".");

        for (Object obj : objects) {

            sb.append(obj.toString());

        }

        System.out.println("keyGenerator=" + sb.toString());

        return sb.toString();

    };

}

@Bean

publicRedisTemplate redisTemplate() {

    RedisTemplate redisTemplate =newRedisTemplate<>();

    redisTemplate.setConnectionFactory(factory);

    GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer =new GenericJackson2JsonRedisSerializer();

    redisTemplate.setKeySerializer(genericJackson2JsonRedisSerializer);

    redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);

    redisTemplate.setHashKeySerializer(new StringRedisSerializer());

    redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);

    return redisTemplate;

}

@Bean

@Override

public CacheResolver cacheResolver() {

    return new SimpleCacheResolver(cacheManager());

}

@Bean

@Override

public CacheErrorHandler errorHandler() {

    // 用于捕獲從Cache中進(jìn)行CRUD時(shí)的異常的回調(diào)處理器溅话。

   return  new SimpleCacheErrorHandler();

}

@Bean

@Override

public CacheManager cacheManager() {

    RedisCacheConfiguration cacheConfiguration =    defaultCacheConfig()

                    .disableCachingNullValues()

                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new         GenericJackson2JsonRedisSerializer()));

    return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();

}

}

代碼使用

測試@Cacheable方法

@Test

publicvoid findUserTest() {

    for(inti = 0; i < 3; i++) {

        System.out.println("第" + i + "次");

        User user = userService.findUser();

        System.out.println(user);

    }

}

@Override

@Cacheable(value = {"valueName", "valueName2"}, key = "'keyName1'")

public User findUser() {

    System.out.println("執(zhí)行方法...");

    returnnewUser("id1", "張三", "深圳", "1234567", 18);

}

執(zhí)行結(jié)果

只有一次輸出了'執(zhí)行方法...',后面直接從緩存獲取,不會(huì)再進(jìn)入方法歌焦。

第0次

執(zhí)行方法...

User{id='id1', name='張三', address='深圳', tel='1234567', age=18}

第1次

User{id='id1', name='張三', address='深圳', tel='1234567', age=18}

第2次

User{id='id1', name='張三', address='深圳', tel='1234567', age=18}

image

測試@CachePut方法:對(duì)緩存進(jìn)行了修改

@Test

publicvoid updateUserTest() {

    userService.updateUser();

    User user = userService.findUser();

    System.out.println(user);

}

@Override

@CachePut(value = "valueName", key = "'keyName1'")

public User updateUser() {

    System.out.println("更新用戶...");

    returnnewUser("id1", "李四", "北京", "1234567", 18);

}

執(zhí)行結(jié)果

對(duì)緩存進(jìn)行了更新飞几,獲取值的時(shí)候取了新的值

更新用戶...

User{id='id1', name='李四', address='北京', tel='1234567', age=18}

image

測試@CacheEvict方法:緩存被清空,再次findUser的時(shí)候又重新執(zhí)行了方法独撇。

@Test

publicvoid clearUserTest() {

    userService.clearUser();

    User user = userService.findUser();

    System.out.println(user);

}

@Override

@CacheEvict(value = "valueName",allEntries =true)

publicvoid clearUser() {

    System.out.println("清除緩存...");

}

執(zhí)行結(jié)果

這里清除了緩存屑墨,為什么還是沒有執(zhí)行方法呢?因?yàn)檫@個(gè)方法我們定了兩個(gè)value值纷铣,清了一個(gè)還有一個(gè)

清除緩存...

User{id='id1', name='張三', address='深圳', tel='1234567', age=18}

image

最后貼一下代碼吧

User.java

package com.wwj.springboot.model;import java.io.Serializable;/** * @author wwj

*/publicclassUserimplements Serializable {

public User() {

}

private String id;

private String name;

private String address;

private String tel;

private Integer age;

//省略get,set,tostring}

CacheTest.java

package com.wwj.springboot.cache;import com.wwj.springboot.model.User;import com.wwj.springboot.service.UserService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.cache.annotation.EnableCaching;import org.springframework.test.context.junit4.SpringRunner;import javax.annotation.Resource;/** * @author wwj

*/@RunWith(SpringRunner.class)

@SpringBootTest

@EnableCachingpublicclass CacheTest {

@Resource

private UserService userService;

@Test

publicvoid findUserTest() {

    for(inti = 0; i < 3; i++) {

        System.out.println("第" + i + "次");

        User user = userService.findUser();

        System.out.println(user);

    }

}

@Test

publicvoid updateUserTest() {

    userService.updateUser();

    User user = userService.findUser();

    System.out.println(user);

}

@Test

publicvoid clearUserTest() {

    userService.clearUser();

    User user = userService.findUser();

    System.out.println(user);

}

}

UserService.java

package com.wwj.springboot.service;import com.wwj.springboot.model.User;import java.util.List;/** * @author wwj

*/publicinterface UserService {

/**    * 獲取用戶

* @return user

*/    User findUser();

/**    * 更新用戶信息

* @return user

*/    User updateUser();

/**    * 清除緩存的用戶信息

*/void clearUser();

}

UserServiceImpl.java

package com.wwj.springboot.service.impl;import com.wwj.springboot.model.User;import com.wwj.springboot.service.UserService;import org.springframework.cache.annotation.CacheConfig;import org.springframework.cache.annotation.CacheEvict;import org.springframework.cache.annotation.CachePut;import org.springframework.cache.annotation.Cacheable;import org.springframework.stereotype.Service;

/**

  • @author wwj

*/

@Service

@CacheConfig(cacheNames = "CacheConfigName")publicclassUserServiceImplimplements UserService {

@Override

@Cacheable(value = {"valueName", "valueName2"}, key = "'keyName1'")

public User findUser() {

    System.out.println("執(zhí)行方法...");

    return new User("id1", "張三", "深圳", "1234567", 18);

}

@Override

@CachePut(value = "valueName", key = "'keyName1'")

public User updateUser() {

    System.out.println("更新用戶...");

    returnnewUser("id1", "李四", "北京", "1234567", 18);

}

@Override

@CacheEvict(value = "valueName",allEntries =true)

publicvoid clearUser() {

    System.out.println("清除緩存...");

}

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末卵史,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子搜立,更是在濱河造成了極大的恐慌以躯,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啄踊,死亡現(xiàn)場離奇詭異寸潦,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)社痛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門见转,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蒜哀,你說我怎么就攤上這事斩箫±羯埃” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵乘客,是天一觀的道長狐血。 經(jīng)常有香客問我,道長易核,這世上最難降的妖魔是什么匈织? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮牡直,結(jié)果婚禮上缀匕,老公的妹妹穿的比我還像新娘。我一直安慰自己碰逸,他們只是感情好乡小,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著饵史,像睡著了一般满钟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胳喷,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天湃番,我揣著相機(jī)與錄音,去河邊找鬼吭露。 笑死吠撮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的奴饮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼择浊,長吁一口氣:“原來是場噩夢啊……” “哼戴卜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起琢岩,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤投剥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后担孔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體江锨,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年糕篇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了啄育。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拌消,死狀恐怖挑豌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤氓英,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布侯勉,位于F島的核電站,受9級(jí)特大地震影響铝阐,放射性物質(zhì)發(fā)生泄漏址貌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一徘键、第九天 我趴在偏房一處隱蔽的房頂上張望练对。 院中可真熱鬧,春花似錦啊鸭、人聲如沸锹淌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赂摆。三九已至,卻和暖如春钟些,著一層夾襖步出監(jiān)牢的瞬間烟号,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來泰國打工政恍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汪拥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓篙耗,卻偏偏與公主長得像迫筑,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宗弯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

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