Springboot中Spring-cache與redis整合

也是在整合redis的時候偶然間發(fā)現(xiàn)spring-cache的模软。這也是一個不錯的框架伟骨,與spring的事務(wù)使用類似,只要添加一些注解方法燃异,就可以動態(tài)的去操作緩存了底靠,減少代碼的操作。如果這些注解不滿足項目的需求特铝,我們也可以參考spring-cache的實現(xiàn)思想,使用AOP代理+緩存操作來管理緩存的使用壹瘟。
在這個例子中我使用的是redis鲫剿,當然,因為spring-cache的存在稻轨,我們可以整合多樣的緩存技術(shù)灵莲,例如Ecache、Mamercache等殴俱。
下面來看springcache的具體操作吧政冻!
附上官方的文檔:
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html

redis中整合spring-cache

我代碼的工程還是接著上個項目來使用的, 所以可以結(jié)合上一篇博客來看
http://blog.csdn.net/u011521890/article/details/78070773
或者直接找github线欲,歡迎star
https://github.com/hpulzl/book_recommend

緩存的配置如下

  • 在RedisCacheConfig上添加注解
@EnableCaching //加上這個注解是的支持緩存注解
  • 創(chuàng)建RedisCacheManager
 /**
     * 設(shè)置RedisCacheManager
     * 使用cache注解管理redis緩存
     *
     * @return
     */
    @Bean
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate());
        return redisCacheManager;
    }
  • 自定義緩存的key
 /**
     * 自定義生成redis-key
     *
     * @return
     */
    @Override
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... 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();
            }
        };
    }

在RedisCacheConfig中添加以上的代碼明场,就可以使用springcache的注解了。下面介紹springcache的注解如何使用

spring cache與redis緩存結(jié)合

對springCache概念的了解

springCache支持透明的添加緩存到應(yīng)用程序李丰,類似事務(wù)處理一般苦锨,不需要復雜的代碼支持。

緩存的主要使用方式包括以下兩方面

  1. 緩存的聲明,需要根據(jù)項目需求來妥善的應(yīng)用緩存
  2. 緩存的配置方式舟舒,選擇需要的緩存支持拉庶,例如Ecache、redis秃励、memercache等

緩存的注解介紹

@Cacheable 觸發(fā)緩存入口

@CacheEvict 觸發(fā)移除緩存

@CacahePut 更新緩存

@Caching 將多種緩存操作分組

@CacheConfig 類級別的緩存注解氏仗,允許共享緩存名稱

@CacheConfig

該注解是可以將緩存分類,它是類級別的注解方式夺鲜。我們可以這么使用它皆尔。
這樣的話,UseCacheRedisService的所有緩存注解例如@Cacheable的value值就都為user谣旁。

@CacheConfig(cacheNames = "user")
@Service
public class UseCacheRedisService {}

在redis的緩存中顯示如下

127.0.0.1:6379> keys *
1) "user~keys"
2) "user_1"
127.0.0.1:6379> get user~keys
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> type user~keys
zset
127.0.0.1:6379> zrange user~keys 0 10
1) "user_1"

我們注意到床佳,生成的user~keys,它是一個zset類型的key榄审,如果使用get會報WRONGTYPE Operation against a key holding the wrong kind of value砌们。這個問題坑了我很久

@Cacheable

一般用于查詢操作,根據(jù)key查詢緩存.

  1. 如果key不存在搁进,查詢db浪感,并將結(jié)果更新到緩存中。
  2. 如果key存在饼问,直接查詢緩存中的數(shù)據(jù)影兽。

查詢的例子,當?shù)谝徊樵兊臅r候莱革,redis中不存在key峻堰,會從db中查詢數(shù)據(jù),并將返回的結(jié)果插入到redis中盅视。

@Cacheable
    public List<User> selectAllUser(){
        return userMapper.selectAll();
    }

調(diào)用方式捐名。

@Test
    public void selectTest(){
        System.out.println("===========第一次調(diào)用=======");
        List<User> list = useCacheRedisService.selectAllUser();
        System.out.println("===========第二次調(diào)用=======");
        List<User> list2 = useCacheRedisService.selectAllUser();
        for (User u : list2){
            System.out.println("u = " + u);
        }
    }

打印結(jié)果,大家也可以試一試
只輸出一次sql查詢的語句闹击,說明第二次查詢是從redis中查到的镶蹋。

===========第一次調(diào)用=======
keyGenerator=com.lzl.redisService.UseCacheRedisService.selectAllUser.
keyGenerator=com.lzl.redisService.UseCacheRedisService.selectAllUser.
DEBUG [main] - ==>  Preparing: SELECT id,name,sex,age,password,account FROM user 
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==      Total: 1
===========第二次調(diào)用=======
keyGenerator=com.lzl.redisService.UseCacheRedisService.selectAllUser.
u = User{id=1, name='fsdfds', sex='fdsfg', age=24, password='gfdsg', account='gfds'}

redis中的結(jié)果
我們可以看到redis中已經(jīng)存在
com.lzl.redisService.UseCacheRedisService.selectAllUser.記錄了。
這么長的一串字符key是根據(jù)自定義key值生成的赏半。

127.0.0.1:6379> keys *
1) "user~keys"
2) "com.lzl.redisService.UseCacheRedisService.selectAllUser."
3) "user_1"
127.0.0.1:6379> get com.lzl.redisService.UseCacheRedisService.selectAllUser.
"[\"java.util.ArrayList\",[[\"com.lzl.bean.User\",{\"id\":1,\"name\":\"fsdfds\",\"sex\":\"fdsfg\",\"age\":24,\"password\":\"gfdsg\",\"account\":\"gfds\"}]]]"

@CachePut

一般用于更新和插入操作贺归,每次都會請求db
通過key去redis中進行操作。

  1. 如果key存在断箫,更新內(nèi)容
  2. 如果key不存在拂酣,插入內(nèi)容。
 /**
     * 單個user對象的插入操作仲义,使用user+id
     * @param user
     * @return
     */
    @CachePut(key = "\"user_\" + #user.id")
    public User saveUser(User user){
        userMapper.insert(user);
        return user;
    }

redis中的結(jié)果
多了一條記錄user_2

127.0.0.1:6379> keys *
1) "user~keys"
2) "user_2"
3) "com.lzl.redisService.UseCacheRedisService.selectAllUser."
4) "user_1"
127.0.0.1:6379> get user_2
"[\"com.lzl.bean.User\",{\"id\":2,\"name\":\"fsdfds\",\"sex\":\"fdsfg\",\"age\":24,\"password\":\"gfdsg\",\"account\":\"gfds\"}]"

@CacheEvict

根據(jù)key刪除緩存中的數(shù)據(jù)踱葛。allEntries=true表示刪除緩存中的所有數(shù)據(jù)丹莲。

 @CacheEvict(key = "\"user_\" + #id")
    public void deleteById(Integer id){
        userMapper.deleteByPrimaryKey(id);
    }

測試方法

 @Test
    public void deleteTest(){
        useCacheRedisService.deleteById(1);
    }

redis中的結(jié)果
user_1已經(jīng)移除掉。

127.0.0.1:6379> keys *
1) "user~keys"
2) "user_2"
3) "com.lzl.redisService.UseCacheRedisService.selectAllUser."

測試allEntries=true時的情形尸诽。

 @Test
    public void deleteAllTest(){
        useCacheRedisService.deleteAll();
    }
 @CacheEvict(allEntries = true)
    public void deleteAll(){
        userMapper.deleteAll();
    }

redis中的結(jié)果
redis中的數(shù)據(jù)已經(jīng)全部清空

127.0.0.1:6379> keys *
(empty list or set)

@Caching

通過注解的屬性值可以看出來甥材,這個注解將其他注解方式融合在一起了,我們可以根據(jù)需求來自定義注解性含,并將前面三個注解應(yīng)用在一起

public @interface Caching {
    Cacheable[] cacheable() default {};

    CachePut[] put() default {};

    CacheEvict[] evict() default {};
}

使用例子如下

 @Caching(
            put = {
                    @CachePut(value = "user", key = "\"user_\" + #user.id"),
                    @CachePut(value = "user", key = "#user.name"),
                    @CachePut(value = "user", key = "#user.account")
            }
    )
    public User saveUserByCaching(User user){
        userMapper.insert(user);
        return user;
    }
 @Test
    public void saveUserByCachingTest(){
        User user = new User();
        user.setName("dkjd");
        user.setAccount("dsjkf");
        useCacheRedisService.saveUserByCaching(user);
    }

redis中的執(zhí)行結(jié)果
一次添加三個key

127.0.0.1:6379> keys *
1) "user~keys"
2) "dsjkf"
3) "dkjd"
4) "user_3"

結(jié)合@Caching還可以設(shè)置自定義的注解

自定義緩存注解

@Caching(
        put = {
                @CachePut(value = "user", key = "\"user_\" + #user.id"),
                @CachePut(value = "user", key = "#user.name"),
                @CachePut(value = "user", key = "#user.account")
        }
)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SaveUserInfo {

}

使用如下

@SaveUserInfo
    public User saveUserByInfo(User user){
        userMapper.insert(user);
        return user;
    }

測試

@Test
    public void saveUserByInfoTest(){
        User user = new User();
        user.setName("haha");
        user.setAccount("hhhcc");
        useCacheRedisService.saveUserByInfo(user);
    }

redis結(jié)果

127.0.0.1:6379> keys *
1) "user_4"
2) "dsjkf"
3) "dkjd"
4) "user~keys"
5) "haha"
6) "hhhcc"
7) "user_3"

通過以上的例子基本可以了解springcache的使用了洲赵,當然還有更加復雜的操作,這里只是簡單的介紹一下商蕴,運用到實際的項目中還是有所欠缺的叠萍。不過有這個基礎(chǔ)應(yīng)該不會太難。同時有時間可以再研究一下spring-cache的實現(xiàn)原理绪商。是基于AOP的實現(xiàn)的苛谷,這也是我們在項目中學習的地方。

遇到的兩個問題

WRONGTYPE Operation against a key holding the wrong kind of value

這個就是上面所說的類型不一致格郁,使用redis命令不當造成的腹殿。所以在查找redis的value時候,需要知道key的類型例书。

type key

Invalid argument(s)

還是redis現(xiàn)實的錯誤锣尉,這個有些困惑,在get的時候决采,一定要加上""(引號)才行自沧。

127.0.0.1:6379> keys *
1) "user_4"
2) "com.lzl.redisService.UseCacheRedisService.saveUserByErr.User{id=5, name='fsdsg', sex='vcxvx', age=24, password='vcxvcxc', account='vxcvxc'}"
3) "dsjkf"
4) "dkjd"
5) "user~keys"
6) "haha"
7) "hhhcc"
8) "user_3"
127.0.0.1:6379> get com.lzl.redisService.UseCacheRedisService.saveUserByErr.User{id=5, name='fsdsg', sex='vcxvx', age=24, password='vcxvcxc', account='vxcvxc'}
Invalid argument(s)
127.0.0.1:6379> type com.lzl.redisService.UseCacheRedisService.saveUserByErr.User{id=5, name='fsdsg', sex='vcxvx', age=24, password='vcxvcxc', account='vxcvxc'}
Invalid argument(s)
127.0.0.1:6379> get "com.lzl.redisService.UseCacheRedisService.saveUserByErr.User{id=5, name='fsdsg', sex='vcxvx', age=24, password='vcxvcxc', account='vxcvxc'}"
"[\"com.lzl.bean.User\",{\"id\":5,\"name\":\"fsdsg\",\"sex\":\"vcxvx\",\"age\":24,\"password\":\"vcxvcxc\",\"account\":\"vxcvxc\"}]"
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市树瞭,隨后出現(xiàn)的幾起案子拇厢,更是在濱河造成了極大的恐慌,老刑警劉巖晒喷,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孝偎,死亡現(xiàn)場離奇詭異,居然都是意外死亡厨埋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門捐顷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荡陷,“玉大人,你說我怎么就攤上這事迅涮》显蓿” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵叮姑,是天一觀的道長唉地。 經(jīng)常有香客問我据悔,道長,這世上最難降的妖魔是什么耘沼? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任极颓,我火速辦了婚禮,結(jié)果婚禮上群嗤,老公的妹妹穿的比我還像新娘菠隆。我一直安慰自己,他們只是感情好狂秘,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布骇径。 她就那樣靜靜地躺著,像睡著了一般者春。 火紅的嫁衣襯著肌膚如雪破衔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天钱烟,我揣著相機與錄音晰筛,去河邊找鬼。 笑死忠售,一個胖子當著我的面吹牛传惠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播稻扬,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼卦方,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了泰佳?” 一聲冷哼從身側(cè)響起盼砍,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎逝她,沒想到半個月后浇坐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡黔宛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年近刘,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片臀晃。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡觉渴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出徽惋,到底是詐尸還是另有隱情案淋,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布险绘,位于F島的核電站踢京,受9級特大地震影響誉碴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瓣距,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一黔帕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧旨涝,春花似錦蹬屹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至弧腥,卻和暖如春厦取,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背管搪。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工虾攻, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人更鲁。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓霎箍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親澡为。 傳聞我的和親對象是個殘疾皇子漂坏,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

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