SpringBoot | 第十一章:Redis 的集成和簡(jiǎn)單使用

原文出處: oKong

前言

上幾節(jié)講了利用Mybatis-Plus這個(gè)第三方的ORM框架進(jìn)行數(shù)據(jù)庫訪問周叮,在實(shí)際工作中欺矫,在存儲(chǔ)一些非結(jié)構(gòu)化或者緩存一些臨時(shí)數(shù)據(jù)及熱點(diǎn)數(shù)據(jù)時(shí),一般上都會(huì)用上mongodbredis進(jìn)行這方面的需求。所以這一章節(jié)準(zhǔn)備講下緩存數(shù)據(jù)庫Redis的集成霹肝,同時(shí)會(huì)介紹下基于Redis和注解驅(qū)動(dòng)的Spring Cache的簡(jiǎn)單使用。

Redis 介紹

大家應(yīng)該對(duì)Redis應(yīng)該比較熟悉了塑煎。這幾年也是大行其道的緩存數(shù)據(jù)庫袖牙,目前的memcached由于使用場(chǎng)景及其存儲(chǔ)數(shù)據(jù)結(jié)構(gòu)的單一(不知道現(xiàn)在是否有改善赂韵,現(xiàn)在基本沒有接觸了),在工作中也使用的少了。引用官網(wǎng)的簡(jiǎn)介,Redis是一個(gè)開源的使用ANSI C語言編寫繁扎、遵守BSD協(xié)議、支持網(wǎng)絡(luò)豁跑、可基于內(nèi)存亦可持久化的日志型力细、Key-Value數(shù)據(jù)庫,并提供多種語言的API雀哨。

推薦redis中國(guó)社區(qū):http://www.redis.cn/

SpringBoot的Redis集成

0.本章節(jié)以上一章節(jié)的示例基礎(chǔ)上進(jìn)行集成磕谅。所以大家可下載第十章節(jié)示例或者在章節(jié)末尾直接下載本章節(jié)示例私爷。

1.pom依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

直接引入,相關(guān)依賴會(huì)自動(dòng)加載的膊夹,這就是springboot讓人愉悅之處呀衬浑。

2.application.properties配置加入redis相關(guān)配置

配置自動(dòng)加載類為:org.springframework.boot.autoconfigure.data.redis.RedisProperties,可在屬性文件中點(diǎn)擊某屬性快捷跳轉(zhuǎn)放刨。注意到其啟動(dòng)類為org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration工秩。這里就不介紹了,后面會(huì)寫一篇關(guān)于Springboot自動(dòng)加載配置的文章宏榕。

# REDIS (RedisProperties)
# Redis數(shù)據(jù)庫索引(默認(rèn)為0)
spring.redis.database=0
# Redis服務(wù)器地址
spring.redis.host=127.0.0.1
# Redis服務(wù)器連接端口
spring.redis.port=6379 
# Redis服務(wù)器連接密碼(默認(rèn)為空)
spring.redis.password=
# 連接池最大連接數(shù)(使用負(fù)值表示沒有限制)
spring.redis.pool.max-active=8 
# 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒有限制)
spring.redis.pool.max-wait=-1 
# 連接池中的最大空閑連接
spring.redis.pool.max-idle=8 
# 連接池中的最小空閑連接
spring.redis.pool.min-idle=0 
# 連接超時(shí)時(shí)間(毫秒)
spring.redis.timeout=0

3.一般上通過以上兩步就可使用了拓诸,但工作中一般上是通過StringRedisTemplate(默認(rèn)采用string的序列化,保存key和值時(shí)都是通過此序列化策略)接口進(jìn)行操作麻昼,所以這里直接配置了StringRedisTemplatebean類奠支。
RedisConfig.java

/**
 * 
 * @author oKong
 *
 */
@Configuration
public class RedisConfig {
     
    /**
     *  定義 StringRedisTemplate ,指定序列化和反序列化的處理類
     * @param factory
     * @return
     */
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(
                Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //序列化 值時(shí)使用此序列化方法
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

4.編寫控制類抚芦,測(cè)試集成是否生效倍谜。
RedisController.java

@RestController
@RequestMapping("/redis")
@Api(tags = "redis 測(cè)試API")
public class RedisController {
 
    @Autowired
    StringRedisTemplate redisTemplate;
     
    @GetMapping("set/{key}/{value}")
    @ApiOperation(value="設(shè)置緩存")
    public String set(@PathVariable("key")String key,@PathVariable("value") String value) {
        //注意這里的 key不能為null spring 內(nèi)部有檢驗(yàn)
        redisTemplate.opsForValue().set(key, value);
        return key + "," + value;
    }
     
    @GetMapping("get/{key}")
    @ApiOperation(value="根據(jù)key獲取緩存")
    public String get(@PathVariable("key") String key) {
         
        return "key=" + key + ",value=" + redisTemplate.opsForValue().get(key);
    }
}

5.訪問:http://127.0.0.1:8080/swagger-ui.html。 也可直接瀏覽器輸入:

set值

set值

get值

get值

瀏覽器訪問

查看redis記錄:

redis記錄

至此叉抡,redis就集成好了尔崔。實(shí)際中可根據(jù)業(yè)務(wù)需要進(jìn)行相關(guān)操作,比如緩存session記錄褥民,緩存菜單列表等季春。

Spring Cache 和 redis 使用。

Spring CacheSpring框架提供的對(duì)緩存使用的抽象類消返,支持多種緩存载弄,比如RedisEHCache等撵颊,集成很方便宇攻。同時(shí)提供了多種注解來簡(jiǎn)化緩存的使用,可對(duì)方法進(jìn)行緩存倡勇。

0.修改RedisConfig配置類逞刷,加入注解@EnableCaching,同時(shí)設(shè)置CacheManager緩存管理類妻熊,這里使用RedisCacheManager夸浅,其他的管理類還有:SimpleCacheManagerConcurrentMapCacheManager等固耘,默認(rèn)提供的在類org.springframework.cache.support下题篷,可自行查閱。

/**
 * 
 * @author oKong
 *
 */
@Configuration
@EnableCaching
public class RedisConfig {
     
    /**
     *  定義 StringRedisTemplate 厅目,指定序列號(hào)和反序列化的處理類
     * @param factory
     * @return
     */
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(
                Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //序列化 值時(shí)使用此序列化方法
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
     
    @Bean
    public CacheManager cacheManager(RedisTemplate<String,String> redisTemplate) {
        RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
        //使用前綴
        rcm.setUsePrefix(true);
        //緩存分割符 默認(rèn)為 ":"
//        rcm.setCachePrefix(new DefaultRedisCachePrefix(":"));
        //設(shè)置緩存過期時(shí)間
        //rcm.setDefaultExpiration(60);//秒
        return rcm;
    }
}

1.改造UserController控制層番枚,引入@Cacheable等注解法严。

/**
 * 用戶控制層 簡(jiǎn)單演示增刪改查及分頁
 * 新增了swagger文檔內(nèi)容 2018-07-21
 * 新增了@caching使用 2018-07-23
 * @author oKong
 *
 */
@RestController
@RequestMapping("/user")
@Api(tags="用戶API")
public class UserController {
 
    @Autowired
    IUserService userService;
     
    @PostMapping("add")
    @ApiOperation(value="用戶新增")
    //正常業(yè)務(wù)時(shí), 需要在user類里面進(jìn)行事務(wù)控制葫笼,控制層一般不進(jìn)行業(yè)務(wù)控制的深啤。
    //@Transactional(rollbackFor = Exception.class)
    public Map<String,String> addUser(@Valid @RequestBody UserReq userReq){
         
        User user = new User();
        user.setCode(userReq.getCode());
        user.setName(userReq.getName());
        //由于設(shè)置了主鍵策略 id可不用賦值 會(huì)自動(dòng)生成
        //user.setId(0L);
        userService.insert(user);
        Map<String,String> result = new HashMap<String,String>();
        result.put("respCode", "01");
        result.put("respMsg", "新增成功");
        //事務(wù)測(cè)試
        //System.out.println(1/0);
        return result;
    }
     
    @PostMapping("update")
    @ApiOperation(value="用戶修改")    
    //更新時(shí) 直接刪除緩存 以保證下次獲取時(shí)先從數(shù)據(jù)庫中獲取最新數(shù)據(jù)
    @CacheEvict(value="OKONG", key="#userReq.id")
    public Map<String,String> updateUser(@Valid @RequestBody UserReq userReq){
         
        if(userReq.getId() == null || "".equals(userReq.getId())) {
            throw new CommonException("0000", "更新時(shí)ID不能為空");
        }
        User user = new User();
        user.setCode(userReq.getCode());
        user.setName(userReq.getName());
        user.setId(Long.parseLong(userReq.getId()));        
        userService.updateById(user);
        Map<String,String> result = new HashMap<String,String>();
        result.put("respCode", "01");
        result.put("respMsg", "更新成功");
        return result;
    }
     
    @GetMapping("/get/{id}")
    @ApiOperation(value="用戶查詢(ID)")    
    @ApiImplicitParam(name="id",value="查詢ID",required=true)
    @Cacheable(value="OKONG",key="#id")
    public Map<String,Object> getUser(@PathVariable("id") String id){
        //查詢
        User user = userService.selectById(id);
        if(user == null) {
            throw new CommonException("0001", "用戶ID:" + id + ",未找到");
        }
        UserResp resp = UserResp.builder()
                .id(user.getId().toString())
                .code(user.getCode())
                .name(user.getName())
                .status(user.getStatus())
                .build();
        Map<String,Object> result = new HashMap<String,Object>();
        result.put("respCode", "01");
        result.put("respMsg", "成功");
        result.put("data", resp);
        return result;
    }
     
    @GetMapping("/page")
    @ApiOperation(value="用戶查詢(分頁)")        
    public Map<String,Object> pageUser(int current, int size){
        //分頁
        Page<User> page = new Page<>(current, size);
        Map<String,Object> result = new HashMap<String,Object>();
        result.put("respCode", "01");
        result.put("respMsg", "成功");
        result.put("data", userService.selectPage(page));
        return result;
    }
         
}

2.利用Swagger控制頁面路星,新增一個(gè)用戶溯街,然后獲取用戶,會(huì)發(fā)現(xiàn)緩存里已經(jīng)有此id的用戶數(shù)據(jù)了洋丐。

第一次獲取

redis查看:

redis

再次獲取呈昔,會(huì)發(fā)現(xiàn)這次沒有直接訪問數(shù)據(jù)庫了,而是直接從緩存讀取友绝。大家可在觀察下控制臺(tái)的輸出情況(可先清空控制臺(tái)堤尾,然后在請(qǐng)求)。

控制臺(tái)

此時(shí)控制臺(tái)無任何輸出迁客,但前端已經(jīng)獲取到值了郭宝。

關(guān)于SpringCache 注解的簡(jiǎn)單介紹

  • @Cacheable:標(biāo)記在一個(gè)方法上,也可以標(biāo)記在一個(gè)類上掷漱。主要是緩存標(biāo)注對(duì)象的返回結(jié)果粘室,標(biāo)注在方法上緩存該方法的返回值,標(biāo)注在類上卜范,緩存該類所有的方法返回值衔统。
    參數(shù): value緩存名、 key緩存鍵值海雪、 condition滿足緩存條件缰冤、unless否決緩存條件

  • @CacheEvict:從緩存中移除相應(yīng)數(shù)據(jù)。

  • @CachePut:方法支持緩存功能喳魏。與@Cacheable不同的是使用@CachePut標(biāo)注的方法在執(zhí)行前不會(huì)去檢查緩存中是否存在之前執(zhí)行過的結(jié)果,而是每次都會(huì)執(zhí)行該方法怀薛,并將執(zhí)行結(jié)果以鍵值對(duì)的形式存入指定的緩存中刺彩。

  • @Caching:多個(gè)Cache注解使用,比如新增用戶時(shí),刪除用戶屬性等需要?jiǎng)h除或者更新多個(gè)緩存時(shí)枝恋,集合以上三個(gè)注解创倔。

常用的就以上幾個(gè),對(duì)于@CacheConfig沒使用過焚碌,這里就不說明了畦攘。

對(duì)于對(duì)幾個(gè)注解類的簡(jiǎn)單使用就結(jié)束了,相關(guān)的詳細(xì)用法十电,比如自定義條件緩存知押,自定義注解等叹螟,這里就不闡述了,請(qǐng)讀者自行

SpEL上下文數(shù)據(jù)

Spring Cache提供了一些供我們使用的SpEL上下文數(shù)據(jù)台盯,下表直接摘自互聯(lián)網(wǎng)

名稱 位置 描述 示例
methodName root對(duì)象 當(dāng)前被調(diào)用的方法名 root.methodName
method root對(duì)象 當(dāng)前被調(diào)用的方法 root.method.name
target root對(duì)象 當(dāng)前被調(diào)用的目標(biāo)對(duì)象 root.target
targetClass root對(duì)象 當(dāng)前被調(diào)用的目標(biāo)對(duì)象類 root.targetClass
args root對(duì)象 當(dāng)前被調(diào)用的方法的參數(shù)列表 root.args[0]
caches root對(duì)象 當(dāng)前方法調(diào)用使用的緩存列表(如@Cacheable(value={“cache1”, “cache2”}))罢绽,則有兩個(gè)cache root.caches[0].name
argument name 執(zhí)行上下文 當(dāng)前被調(diào)用的方法的參數(shù),如findById(Long id)静盅,我們可以通過#id拿到參數(shù) user.id
result 執(zhí)行上下文 方法執(zhí)行后的返回值(僅當(dāng)方法執(zhí)行之后的判斷有效良价,如‘unless’,’cache evict’的beforeInvocation=false) result
@CacheEvict(value = "user", key = "#user.id", condition = "#root.target.canCache() and #root.caches[0].get(#user.id).get().username ne #user.username", beforeInvocation = true) public  void  conditionUpdate(User user)

總結(jié)

本章節(jié)主要是對(duì)redis結(jié)合Spring Cache的集成和簡(jiǎn)單使用進(jìn)行了說明蒿叠,詳細(xì)的用法明垢,可自行搜索相關(guān)資料下,這里就不闡述了市咽。因?yàn)閷?duì)于百分之八十之上的緩存要求基本能滿足了痊银。使用緩存時(shí),一定要注意緩存生命周期的控制魂务,不然容易出現(xiàn)數(shù)據(jù)不一致的情況曼验,謹(jǐn)記!

最后

目前互聯(lián)網(wǎng)上很多大佬都有SpringBoot系列教程粘姜,如有雷同鬓照,請(qǐng)多多包涵了。本文是作者在電腦前一字一句敲的孤紧,每一步都是實(shí)踐的豺裆。若文中有所錯(cuò)誤之處,還望提出号显,謝謝臭猜。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市押蚤,隨后出現(xiàn)的幾起案子蔑歌,更是在濱河造成了極大的恐慌,老刑警劉巖揽碘,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件次屠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡雳刺,警方通過查閱死者的電腦和手機(jī)劫灶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來掖桦,“玉大人本昏,你說我怎么就攤上這事∏雇簦” “怎么了涌穆?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵怔昨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我蒲犬,道長(zhǎng)朱监,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任原叮,我火速辦了婚禮赫编,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘奋隶。我一直安慰自己擂送,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布唯欣。 她就那樣靜靜地躺著嘹吨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪境氢。 梳的紋絲不亂的頭發(fā)上蟀拷,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音萍聊,去河邊找鬼问芬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛寿桨,可吹牛的內(nèi)容都是我干的此衅。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼亭螟,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼挡鞍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起预烙,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤墨微,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后扁掸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體欢嘿,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年也糊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羡宙。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡狸剃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出狗热,到底是詐尸還是另有隱情钞馁,我是刑警寧澤虑省,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站僧凰,受9級(jí)特大地震影響探颈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜训措,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一伪节、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧绩鸣,春花似錦怀大、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至捡多,卻和暖如春蓖康,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背垒手。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工蒜焊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人淫奔。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓山涡,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親唆迁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鸭丛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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