SpringBoot | 第十一章:Redis的集成和簡單使用

前言

上幾節講了利用Mybatis-Plus這個第三方的ORM框架進行數據庫訪問,在實際工做中,在存儲一些非結構化或者緩存一些臨時數據及熱點數據時,通常上都會用上mongodbredis進行這方面的需求。因此這一章節準備講下緩存數據庫Redis的集成,同時會介紹下基於Redis和註解驅動的Spring Cache的簡單使用。html

Redis 介紹

你們應該對Redis應該比較熟悉了。這幾年也是大行其道的緩存數據庫,目前的memcached因爲使用場景及其存儲數據結構的單一(不知道如今是否有改善,如今基本沒有接觸了),在工做中也使用的少了。引用官網的簡介,Redis是一個開源的使用ANSI C語言編寫、遵照BSD協議、支持網絡、可基於內存亦可持久化的日誌型、Key-Value數據庫,並提供多種語言的API。前端

推薦redis中國社區:http://www.redis.cn/java

SpringBoot的Redis集成

0.本章節以上一章節的示例基礎上進行集成。因此你們可下載第十章節示例或者在章節末尾直接下載本章節示例。git

1.pom依賴github

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

直接引入,相關依賴會自動加載的,這就是springboot讓人愉悅之處呀。redis

2.application.properties配置加入redis相關配置spring

配置自動加載類爲:org.springframework.boot.autoconfigure.data.redis.RedisProperties,可在屬性文件中點擊某屬性快捷跳轉。注意到其啓動類爲org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration。這裏就不介紹了,後面會寫一篇關於Springboot自動加載配置的文章。mongodb

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

3.通常上經過以上兩步就可以使用了,但工做中通常上是經過StringRedisTemplate(默認採用string的序列化,保存key和值時都是經過此序列化策略)接口進行操做,因此這裏直接配置了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);
        //序列化 值時使用此序列化方法
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

4.編寫控制類,測試集成是否生效。 RedisController.java瀏覽器

@RestController
@RequestMapping("/redis")
@Api(tags = "redis 測試API")
public class RedisController {

	@Autowired
	StringRedisTemplate redisTemplate;
	
	@GetMapping("set/{key}/{value}")
	@ApiOperation(value="設置緩存")
	public String set(@PathVariable("key")String key,@PathVariable("value") String value) {
		//注意這裏的 key不能爲null spring 內部有檢驗
		redisTemplate.opsForValue().set(key, value);
		return key + "," + value;
	}
	
	@GetMapping("get/{key}")
	@ApiOperation(value="根據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就集成好了。實際中可根據業務須要進行相關操做,好比緩存session記錄,緩存菜單列表等。

Spring Cache 和 redis 使用。

Spring CacheSpring框架提供的對緩存使用的抽象類,支持多種緩存,好比RedisEHCache等,集成很方便。同時提供了多種註解來簡化緩存的使用,可對方法進行緩存。

0.修改RedisConfig配置類,加入註解@EnableCaching,同時設置CacheManager緩存管理類,這裏使用RedisCacheManager,其餘的管理類還有:SimpleCacheManagerConcurrentMapCacheManager等,默認提供的在類org.springframework.cache.support下,可自行查閱。

/**
 * 
 * @author oKong
 *
 */
@Configuration
@EnableCaching
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);
        //序列化 值時使用此序列化方法
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
    
    @Bean
    public CacheManager cacheManager(RedisTemplate<String,String> redisTemplate) {
        RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
        //使用前綴
        rcm.setUsePrefix(true);
        //緩存分割符 默認爲 ":"
//        rcm.setCachePrefix(new DefaultRedisCachePrefix(":"));
        //設置緩存過時時間
        //rcm.setDefaultExpiration(60);//秒
        return rcm;
    }
}

1.改造UserController控制層,引入@Cacheable等註解。

/**
 * 用戶控制層 簡單演示增刪改查及分頁
 * 新增了swagger文檔內容 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="用戶新增")
    //正常業務時, 須要在user類裏面進行事務控制,控制層通常不進行業務控制的。
    //@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());
        //因爲設置了主鍵策略 id可不用賦值 會自動生成
        //user.setId(0L);
        userService.insert(user);
        Map<String,String> result = new HashMap<String,String>();
        result.put("respCode", "01");
        result.put("respMsg", "新增成功");
        //事務測試
        //System.out.println(1/0);
        return result;
    }
    
    @PostMapping("update")
    @ApiOperation(value="用戶修改")    
    //更新時 直接刪除緩存 以保證下次獲取時先從數據庫中獲取最新數據
    @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", "更新時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控制頁面,新增一個用戶,而後獲取用戶,會發現緩存裏已經有此id的用戶數據了。

第一次獲取

redis查看: redis

再次獲取,會發現此次沒有直接訪問數據庫了,而是直接從緩存讀取。你們可在觀察下控制檯的輸出狀況(可先清空控制檯,而後在請求)。

控制檯 此時控制檯無任何輸出,但前端已經獲取到值了。

關於SpringCache 註解的簡單介紹

  • @Cacheable:標記在一個方法上,也能夠標記在一個類上。主要是緩存標註對象的返回結果,標註在方法上緩存該方法的返回值,標註在類上,緩存該類全部的方法返回值。 參數: value緩存名、 key緩存鍵值、 condition知足緩存條件、unless否決緩存條件

  • @CacheEvict:從緩存中移除相應數據。

  • @CachePut:方法支持緩存功能。與@Cacheable不一樣的是使用@CachePut標註的方法在執行前不會去檢查緩存中是否存在以前執行過的結果,而是每次都會執行該方法,並將執行結果以鍵值對的形式存入指定的緩存中。

  • @Caching:多個Cache註解使用,好比新增用戶時,刪除用戶屬性等須要刪除或者更新多個緩存時,集合以上三個註解。

經常使用的就以上幾個,對於@CacheConfig沒使用過,這裏就不說明了。

對於對幾個註解類的簡單使用就結束了,相關的詳細用法,好比自定義條件緩存,自定義註解等,這裏就不闡述了,請讀者自行

SpEL上下文數據

Spring Cache提供了一些供咱們使用的SpEL上下文數據,下表直接摘自互聯網

名稱 位置 描述 示例
methodName root對象 當前被調用的方法名 root.methodName
method root對象 當前被調用的方法 root.method.name
target root對象 當前被調用的目標對象 root.target
targetClass root對象 當前被調用的目標對象類 root.targetClass
args root對象 當前被調用的方法的參數列表 root.args[0]
caches root對象 當前方法調用使用的緩存列表(如@Cacheable(value={「cache1」, 「cache2」})),則有兩個cache root.caches[0].name
argument name 執行上下文 當前被調用的方法的參數,如findById(Long id),咱們能夠經過#id拿到參數 user.id
result 執行上下文 方法執行後的返回值(僅當方法執行以後的判斷有效,如‘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)

總結

本章節主要是對redis結合Spring Cache的集成和簡單使用進行了說明,詳細的用法,可自行搜索相關資料下,這裏就不闡述了。由於對於百分之八十之上的緩存要求基本能知足了。使用緩存時,必定要注意緩存生命週期的控制,否則容易出現數據不一致的狀況,謹記!

最後

目前互聯網上不少大佬都有SpringBoot系列教程,若有雷同,請多多包涵了。本文是做者在電腦前一字一句敲的,每一步都是實踐的。若文中有所錯誤之處,還望提出,謝謝。

老生常談

  • 我的QQ:499452441
  • 微信公衆號:lqdevOps

公衆號

我的博客:https://blog.lqdev.cn

本文原地址:https://blog.lqdev.cn/2018/07/23/springboot/chapter-eleven/

完整示例:chapter-11

相關文章
相關標籤/搜索