Springboot中Spring-cache與redis整合

也是在整合redis的時(shí)候偶然間發(fā)現(xiàn)spring-cache的。這也是一個(gè)不錯(cuò)的框架,與spring的事務(wù)使用類(lèi)似,只要添加一些注解方法,就可以動(dòng)態(tài)的去操作緩存了,減少代碼的操作。如果這些注解不滿(mǎn)足項(xiàng)目的需求,我們也可以參考spring-cache的實(shí)現(xiàn)思想,使用AOP代理+緩存操作來(lái)管理緩存的使用。
在這個(gè)例子中我使用的是redis,當(dāng)然,因?yàn)閟pring-cache的存在,我們可以整合多樣的緩存技術(shù),例如Ecache、Mamercache等。
下面來(lái)看springcache的具體操作吧!
附上官方的文檔:
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html

redis中整合spring-cache

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

緩存的配置如下

  • 在RedisCacheConfig上添加注解
@EnableCaching //加上這個(gè)注解是的支持緩存注解
  • 創(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é)合

對(duì)springCache概念的了解

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

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

  1. 緩存的聲明,需要根據(jù)項(xiàng)目需求來(lái)妥善的應(yīng)用緩存
  2. 緩存的配置方式,選擇需要的緩存支持,例如Ecache、redis、memercache等

緩存的注解介紹

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

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

@CacahePut 更新緩存

@Caching 將多種緩存操作分組

@CacheConfig 類(lèi)級(jí)別的緩存注解,允許共享緩存名稱(chēng)

@CacheConfig

該注解是可以將緩存分類(lèi),它是類(lèi)級(jí)別的注解方式。我們可以這么使用它。
這樣的話(huà),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,它是一個(gè)zset類(lèi)型的key,如果使用get會(huì)報(bào)WRONGTYPE Operation against a key holding the wrong kind of value。這個(gè)問(wèn)題坑了我很久

@Cacheable

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

  1. 如果key不存在,查詢(xún)db,并將結(jié)果更新到緩存中。
  2. 如果key存在,直接查詢(xún)緩存中的數(shù)據(jù)。

查詢(xún)的例子,當(dāng)?shù)谝徊樵?xún)的時(shí)候,redis中不存在key,會(huì)從db中查詢(xún)數(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查詢(xún)的語(yǔ)句,說(shuō)明第二次查詢(xún)是從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.記錄了。
這么長(zhǎng)的一串字符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

一般用于更新和插入操作,每次都會(huì)請(qǐng)求db
通過(guò)key去redis中進(jìn)行操作。

  1. 如果key存在,更新內(nèi)容
  2. 如果key不存在,插入內(nèi)容。
 /**
     * 單個(gè)user對(duì)象的插入操作,使用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);
    }

測(cè)試方法

 @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."

測(cè)試allEntries=true時(shí)的情形。

 @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

通過(guò)注解的屬性值可以看出來(lái),這個(gè)注解將其他注解方式融合在一起了,我們可以根據(jù)需求來(lái)自定義注解,并將前面三個(gè)注解應(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é)果
一次添加三個(gè)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;
    }

測(cè)試

@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"

通過(guò)以上的例子基本可以了解springcache的使用了,當(dāng)然還有更加復(fù)雜的操作,這里只是簡(jiǎn)單的介紹一下,運(yùn)用到實(shí)際的項(xiàng)目中還是有所欠缺的。不過(guò)有這個(gè)基礎(chǔ)應(yīng)該不會(huì)太難。同時(shí)有時(shí)間可以再研究一下spring-cache的實(shí)現(xiàn)原理。是基于AOP的實(shí)現(xiàn)的,這也是我們?cè)陧?xiàng)目中學(xué)習(xí)的地方。

遇到的兩個(gè)問(wèn)題

WRONGTYPE Operation against a key holding the wrong kind of value

這個(gè)就是上面所說(shuō)的類(lèi)型不一致,使用redis命令不當(dāng)造成的。所以在查找redis的value時(shí)候,需要知道key的類(lèi)型。

type key

Invalid argument(s)

還是redis現(xiàn)實(shí)的錯(cuò)誤,這個(gè)有些困惑,在get的時(shí)候,一定要加上""(引號(hào))才行。

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)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容