姓名:王玉剛? ? ? ?學(xué)號(hào):17021211232
【嵌牛導(dǎo)讀】:利用Mybatis-Plus這個(gè)第三方的ORM框架進(jìn)行數(shù)據(jù)庫(kù)訪問(wèn)故源,在實(shí)際工作中祭钉,在存儲(chǔ)一些非結(jié)構(gòu)化或者緩存一些臨時(shí)數(shù)據(jù)及熱點(diǎn)數(shù)據(jù)時(shí),一般上都會(huì)用上mongodb和redis進(jìn)行這方面的需求。所以這一章節(jié)準(zhǔn)備講下緩存數(shù)據(jù)庫(kù)Redis的集成威恼,同時(shí)會(huì)介紹下基于Redis和注解驅(qū)動(dòng)的Spring Cache的簡(jiǎn)單使用。
【嵌牛鼻子】:Redis? SpringBoot
【嵌牛提問(wèn)】:SpringBoot還有其他的相關(guān)軟件集成嗎?
【嵌牛正文】:SpringBoot的Redis集成
1.pom依賴(lài)
org.springframework.bootspring-boot-starter-data-redis
直接引入箫措,相關(guān)依賴(lài)會(huì)自動(dòng)加載的腹备,這就是springboot讓人愉悅之處呀。
2.application.properties配置加入redis相關(guān)配置
配置自動(dòng)加載類(lèi)為:org.springframework.boot.autoconfigure.data.redis.RedisProperties斤蔓,可在屬性文件中點(diǎn)擊某屬性快捷跳轉(zhuǎn)植酥。注意到其啟動(dòng)類(lèi)為org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration。這里就不介紹了弦牡,后面會(huì)寫(xiě)一篇關(guān)于Springboot自動(dòng)加載配置的文章友驮。
# REDIS (RedisProperties)# Redis數(shù)據(jù)庫(kù)索引(默認(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ù)值表示沒(méi)有限制)spring.redis.pool.max-active=8# 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒(méi)有限制)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.一般上通過(guò)以上兩步就可使用了,但工作中一般上是通過(guò)StringRedisTemplate(默認(rèn)采用string的序列化驾锰,保存key和值時(shí)都是通過(guò)此序列化策略)接口進(jìn)行操作卸留,所以這里直接配置了StringRedisTemplatebean類(lèi)。?
/** *? *@authoroKong * */@ConfigurationpublicclassRedisConfig{/**? ? *? 定義 StringRedisTemplate 稻据,指定序列化和反序列化的處理類(lèi)? ? *@paramfactory? ? *@return*/@BeanpublicRedisTemplateredisTemplate(RedisConnectionFactory factory){? ? ? ? StringRedisTemplate template =newStringRedisTemplate(factory);? ? ? ? Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =newJackson2JsonRedisSerializer<>(? ? ? ? ? ? ? ? Object.class);? ? ? ? ObjectMapper om =newObjectMapper();? ? ? ? om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);? ? ? ? om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);? ? ? ? jackson2JsonRedisSerializer.setObjectMapper(om);//序列化 值時(shí)使用此序列化方法template.setValueSerializer(jackson2JsonRedisSerializer);? ? ? ? template.afterPropertiesSet();returntemplate;? ? }}
4.編寫(xiě)控制類(lèi)艾猜,測(cè)試集成是否生效。
@RestController@RequestMapping("/redis")@Api(tags ="redis 測(cè)試API")publicclassRedisController{@AutowiredStringRedisTemplate redisTemplate;@GetMapping("set/{key}/{value}")@ApiOperation(value="設(shè)置緩存")publicStringset(@PathVariable("key")String key,@PathVariable("value")String value){//注意這里的 key不能為null spring 內(nèi)部有檢驗(yàn)redisTemplate.opsForValue().set(key, value);returnkey +","+ value;}@GetMapping("get/{key}")@ApiOperation(value="根據(jù)key獲取緩存")publicStringget(@PathVariable("key")String key){return"key="+ key +",value="+ redisTemplate.opsForValue().get(key);}}
5.set值?
get值?
查看redis記錄:
至此捻悯,redis就集成好了匆赃。實(shí)際中可根據(jù)業(yè)務(wù)需要進(jìn)行相關(guān)操作,比如緩存session記錄今缚,緩存菜單列表等算柳。
Spring Cache 和 redis 使用。
Spring Cache是Spring框架提供的對(duì)緩存使用的抽象類(lèi)姓言,支持多種緩存瞬项,比如Redis、EHCache等何荚,集成很方便囱淋。同時(shí)提供了多種注解來(lái)簡(jiǎn)化緩存的使用,可對(duì)方法進(jìn)行緩存餐塘。
0.修改RedisConfig配置類(lèi)妥衣,加入注解@EnableCaching,同時(shí)設(shè)置CacheManager緩存管理類(lèi)戒傻,這里使用RedisCacheManager税手,其他的管理類(lèi)還有:SimpleCacheManager、ConcurrentMapCacheManager等需纳,默認(rèn)提供的在類(lèi)org.springframework.cache.support下芦倒,可自行查閱。
/** *? *@authoroKong * */@Configuration@EnableCachingpublicclassRedisConfig{/**? ? *? 定義 StringRedisTemplate 不翩,指定序列號(hào)和反序列化的處理類(lèi)? ? *@paramfactory? ? *@return*/@BeanpublicRedisTemplateredisTemplate(RedisConnectionFactory factory){? ? ? ? StringRedisTemplate template =newStringRedisTemplate(factory);? ? ? ? Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =newJackson2JsonRedisSerializer<>(? ? ? ? ? ? ? ? Object.class);? ? ? ? ObjectMapper om =newObjectMapper();? ? ? ? om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);? ? ? ? om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);? ? ? ? jackson2JsonRedisSerializer.setObjectMapper(om);//序列化 值時(shí)使用此序列化方法template.setValueSerializer(jackson2JsonRedisSerializer);? ? ? ? template.afterPropertiesSet();returntemplate;? ? }@BeanpublicCacheManagercacheManager(RedisTemplate redisTemplate){? ? ? ? RedisCacheManager rcm =newRedisCacheManager(redisTemplate);//使用前綴rcm.setUsePrefix(true);//緩存分割符 默認(rèn)為 ":"http://? ? ? ? rcm.setCachePrefix(new DefaultRedisCachePrefix(":"));//設(shè)置緩存過(guò)期時(shí)間//rcm.setDefaultExpiration(60);//秒returnrcm;? ? }}
1.改造UserController控制層兵扬,引入@Cacheable等注解麻裳。
/** * 用戶控制層 簡(jiǎn)單演示增刪改查及分頁(yè) * 新增了swagger文檔內(nèi)容 2018-07-21 * 新增了@caching使用 2018-07-23 *@authoroKong * */@RestController@RequestMapping("/user")@Api(tags="用戶API")publicclassUserController{@AutowiredIUserService userService;@PostMapping("add")@ApiOperation(value="用戶新增")//正常業(yè)務(wù)時(shí), 需要在user類(lèi)里面進(jìn)行事務(wù)控制周霉,控制層一般不進(jìn)行業(yè)務(wù)控制的掂器。//@Transactional(rollbackFor = Exception.class)publicMapaddUser(@Valid @RequestBody UserReq userReq){? ? ? ? ? ? ? ? User user =newUser();? ? ? ? user.setCode(userReq.getCode());? ? ? ? user.setName(userReq.getName());//由于設(shè)置了主鍵策略 id可不用賦值 會(huì)自動(dòng)生成//user.setId(0L);userService.insert(user);? ? ? ? Map result =newHashMap();? ? ? ? result.put("respCode","01");? ? ? ? result.put("respMsg","新增成功");//事務(wù)測(cè)試//System.out.println(1/0);returnresult;? ? }@PostMapping("update")@ApiOperation(value="用戶修改")//更新時(shí) 直接刪除緩存 以保證下次獲取時(shí)先從數(shù)據(jù)庫(kù)中獲取最新數(shù)據(jù)@CacheEvict(value="OKONG", key="#userReq.id")publicMapupdateUser(@Valid @RequestBody UserReq userReq){if(userReq.getId() ==null||"".equals(userReq.getId())) {thrownewCommonException("0000","更新時(shí)ID不能為空");? ? ? ? }? ? ? ? User user =newUser();? ? ? ? user.setCode(userReq.getCode());? ? ? ? user.setName(userReq.getName());? ? ? ? user.setId(Long.parseLong(userReq.getId()));? ? ? ? ? ? ? ? userService.updateById(user);? ? ? ? Map result =newHashMap();? ? ? ? result.put("respCode","01");? ? ? ? result.put("respMsg","更新成功");returnresult;? ? }@GetMapping("/get/{id}")@ApiOperation(value="用戶查詢(xún)(ID)")@ApiImplicitParam(name="id",value="查詢(xún)ID",required=true)@Cacheable(value="OKONG",key="#id")publicMapgetUser(@PathVariable("id")String id){//查詢(xún)User user = userService.selectById(id);if(user ==null) {thrownewCommonException("0001","用戶ID:"+ id +",未找到");? ? ? ? }? ? ? ? UserResp resp = UserResp.builder()? ? ? ? ? ? ? ? .id(user.getId().toString())? ? ? ? ? ? ? ? .code(user.getCode())? ? ? ? ? ? ? ? .name(user.getName())? ? ? ? ? ? ? ? .status(user.getStatus())? ? ? ? ? ? ? ? .build();? ? ? ? Map result =newHashMap();? ? ? ? result.put("respCode","01");? ? ? ? result.put("respMsg","成功");? ? ? ? result.put("data", resp);returnresult;? ? }@GetMapping("/page")@ApiOperation(value="用戶查詢(xún)(分頁(yè))")publicMappageUser(intcurrent,intsize){//分頁(yè)P(yáng)age page =newPage<>(current, size);? ? ? ? Map result =newHashMap();? ? ? ? result.put("respCode","01");? ? ? ? result.put("respMsg","成功");? ? ? ? result.put("data", userService.selectPage(page));returnresult;? ? }? ? ? ? }
2.利用Swagger控制頁(yè)面俱箱,新增一個(gè)用戶国瓮,然后獲取用戶,會(huì)發(fā)現(xiàn)緩存里已經(jīng)有此id的用戶數(shù)據(jù)了狞谱。
redis查看:?
再次獲取乃摹,會(huì)發(fā)現(xiàn)這次沒(méi)有直接訪問(wèn)數(shù)據(jù)庫(kù)了,而是直接從緩存讀取跟衅。大家可在觀察下控制臺(tái)的輸出情況(可先清空控制臺(tái)孵睬,然后在請(qǐng)求)。
?此時(shí)控制臺(tái)無(wú)任何輸出伶跷,但前端已經(jīng)獲取到值了掰读。
關(guān)于SpringCache 注解的簡(jiǎn)單介紹
@Cacheable:標(biāo)記在一個(gè)方法上,也可以標(biāo)記在一個(gè)類(lèi)上叭莫。主要是緩存標(biāo)注對(duì)象的返回結(jié)果蹈集,標(biāo)注在方法上緩存該方法的返回值,標(biāo)注在類(lèi)上雇初,緩存該類(lèi)所有的方法返回值拢肆。 參數(shù): value緩存名、 key緩存鍵值靖诗、 condition滿足緩存條件郭怪、unless否決緩存條件
@CacheEvict:從緩存中移除相應(yīng)數(shù)據(jù)。
@CachePut:方法支持緩存功能刊橘。與@Cacheable不同的是使用@CachePut標(biāo)注的方法在執(zhí)行前不會(huì)去檢查緩存中是否存在之前執(zhí)行過(guò)的結(jié)果鄙才,而是每次都會(huì)執(zhí)行該方法,并將執(zhí)行結(jié)果以鍵值對(duì)的形式存入指定的緩存中促绵。
@Caching:多個(gè)Cache注解使用,比如新增用戶時(shí)咒循,刪除用戶屬性等需要?jiǎng)h除或者更新多個(gè)緩存時(shí),集合以上三個(gè)注解绞愚。
SpEL上下文數(shù)據(jù)
Spring Cache提供了一些供我們使用的SpEL上下文數(shù)據(jù),下表直接摘自互聯(lián)網(wǎng):
名稱(chēng)位置描述示例
methodNameroot對(duì)象當(dāng)前被調(diào)用的方法名root.methodName
methodroot對(duì)象當(dāng)前被調(diào)用的方法root.method.name
targetroot對(duì)象當(dāng)前被調(diào)用的目標(biāo)對(duì)象root.target
targetClassroot對(duì)象當(dāng)前被調(diào)用的目標(biāo)對(duì)象類(lèi)root.targetClass
argsroot對(duì)象當(dāng)前被調(diào)用的方法的參數(shù)列表root.args[0]
cachesroot對(duì)象當(dāng)前方法調(diào)用使用的緩存列表(如@Cacheable(value={“cache1”, “cache2”}))颖医,則有兩個(gè)cacheroot.caches[0].name
argument name執(zhí)行上下文當(dāng)前被調(diào)用的方法的參數(shù)位衩,如findById(Long id),我們可以通過(guò)#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)publicvoidconditionUpdate(User user)