1. 程式人生 > >spring boot構建基礎版web專案(三)-springboot、redis資料快取整合

spring boot構建基礎版web專案(三)-springboot、redis資料快取整合

寫前說明

在spring+spring mvc+mybatis模式下,使用的最多的就是jedis,但是spring boot整合了redis後,依然可以使用jedis,但是同時也提供了一個RedisTemplate和StringRedisTemplate,RedisTemplate使用的序列化類是預設JdkSerializationRedisSerializer,而StringRedisTemplate使用的序列化類預設是StringRedisSerializer,因此在儲存的方式上也是有所不同。簡單的說就是RedisTemplate的key和value可以是任意型別的資料,但是StringRedisTemplate的key和value只能是String,如果儲存其他型別,序列化和反序列化會存在問題。綜合來說,如果使用spring提供的redis連線就使用RedisTemplate,相容性更高,如果使用jedis就無所謂了,因為它預設就是支援各種資料型別的鍵值。
另外Redis群的搭建可以參考[Redis叢集的簡單搭建和實現(待提供)]。

準備工作

需要在新建立一個模組utui-common,這個模組用於存放Redis封裝的工具類,在utui-dao中加入對utui-common的依賴座標。

依賴的加入
<dependency>
    <groupId>com.springboot.utui</groupId>
    <artifactId>utui-common</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
模組結構
|-utui-common
|-src |-main |-java |-com.springboot.utui.common |-utils |-RedisClientUtil

springboot整合整合RedisTemplate方式實現

父pom.xml

新增spring-boot-starter-redis的依賴jar

<!-- spring整合redis -->
<dependency>
    <groupId
>
org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> <version>1.4.7.RELEASE</version> </dependency>
application.properties
#-------------------------------- redis properties  start --------------------------------#
# redis資料庫索引(預設是0)
spring.redis.datasource=0
# redis伺服器地址
spring.redis.host=127.0.0.1
# redis服務埠
spring.redis.port=6379
# redis伺服器密碼(預設是空)
spring.redis.password=
# 連線池最大連線數(-1表示不限制)
spring.redis.pool.max-active=10
# 連線池最小空閒連線數
spring.redis.pool.max-idle=10
# 連線池最大空閒連線數
spring.redis.pool.min-idle=0
# 最大阻塞等待時間(-1表示不限制,單位:毫秒)
spring.redis.max-wait=3000
# 連線超時時間(單位:毫秒)
spring.redis.timeout=5000
# 叢集節點(三主三從)
# spring.redis.cluster.nodes=127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382,127.0.0.1:6383,127.0.0.1:6384
#-------------------------------- redis properties  end --------------------------------#
RedisClientUtil.java

utui-common下新增RedisClientUtil類,用於封裝Redis的一些公用方法,為了寫這個封裝花了挺長時間,並都測試了一遍,基本沒有問題,如果專案中有使用,需要封裝Redis工具類,可以直接拿去,不謝。

@Component
@Slf4j
public class RedisClientUtil {

    @Autowired
    RedisTemplate<String, String> redisTemplate;

    /**
     * 鍵值對設值
     *
     * @param key   鍵
     * @param value 值
     * @return
     */
    public <K, V> Boolean set(K key, V value) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            connection.set(JSON.toJSONBytes(key), JSON.toJSONBytes(value));
            return true;
        });
    }

    /**
     * 鍵值對設值和有效時間
     *
     * @param key   鍵
     * @param value 值
     * @param time  有效時間(單位:秒)
     * @return
     */
    public <K, V> Boolean setEx(K key, V value, long time) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            connection.setEx(JSON.toJSONBytes(key), time, JSON.toJSONBytes(value));
            return true;
        });
    }

    /**
     * 查詢鍵值對
     *
     * @param key           鍵
     * @param typeReference 返回型別
     * @param <K>           鍵型別
     * @param <R>           返回型別
     * @return
     */
    public <K, R> R get(K key, TypeReference<R> typeReference) {
        byte[] redisValue = redisTemplate.execute((RedisCallback<byte[]>) connection -> connection.get(JSON.toJSONBytes(key)));
        if (redisValue == null) return null;
        return JSONObject.parseObject(new String(redisValue), typeReference);
    }

    /**
     * 儲存Hash結構資料(批量)
     *
     * @param outerKey 外來鍵
     * @param map      內鍵-內值
     * @return
     */
    public <O, I, V> Boolean hSetMap(O outerKey, Map<I, V> map) {
        if (map == null || map.isEmpty()) return false;
        Map<byte[], byte[]> byteMap = new HashMap<>();
        map.forEach((innerKey, innerValue) -> byteMap.put(JSON.toJSONBytes(innerKey), JSON.toJSONBytes(innerValue)));
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            connection.hMSet(JSON.toJSONBytes(outerKey), byteMap);
            return true;
        });
    }

    /**
     * 儲存Hash結構資料
     *
     * @param outerKey   外來鍵
     * @param innerKey   內鍵
     * @param innerValue 值
     * @return
     */
    public <O, I, V> Boolean hSet(O outerKey, I innerKey, V innerValue) {
        Map<I, V> map = new HashMap<>();
        map.put(innerKey, innerValue);
        return this.hSetMap(outerKey, map);
    }

    /**
     * 獲取Hash結構Map集合,內鍵和內值鍵值對封裝成Map集合
     *
     * @param outerKey       外來鍵
     * @param innerKeyType   內鍵型別
     * @param innerValueType 值型別
     * @return
     */
    public <O, I, V> Map<I, V> hGetMap(O outerKey, TypeReference<I> innerKeyType, TypeReference<V> innerValueType) {
        Map<byte[], byte[]> redisMap = redisTemplate.execute
                ((RedisCallback<Map<byte[], byte[]>>) connection -> connection.hGetAll(JSON.toJSONBytes(outerKey)));
        if (redisMap == null) return null;
        Map<I, V> resultMap = new HashMap<>();
        redisMap.forEach((key, value) -> resultMap.put(JSONObject.parseObject
                (new String(key), innerKeyType), JSONObject.parseObject(new String(value), innerValueType)));
        return resultMap;
    }

    /**
     * 查詢Hash結構的值
     *
     * @param outerKey      外來鍵
     * @param innerKey      內鍵
     * @param typeReference 值型別
     * @return
     */
    public <O, I, V> V hGet(O outerKey, I innerKey, TypeReference<V> typeReference) {
        byte[] redisResult = redisTemplate.execute((RedisCallback<byte[]>)
                connection -> connection.hGet(JSON.toJSONBytes(outerKey), JSON.toJSONBytes(innerKey)));
        if (redisResult == null) return null;
        return JSONObject.parseObject(new String(redisResult), typeReference);

    }

    /**
     * 刪除鍵值對
     *
     * @param keys 鍵
     * @return
     */
    public <K> Long del(List<K> keys) {
        if (keys == null || keys.isEmpty()) return 0L;
        byte[][] keyBytes = new byte[keys.size()][];
        int index = 0;
        for (K key : keys) {
            keyBytes[index] = JSON.toJSONBytes(key);
            index++;
        }
        return redisTemplate.execute((RedisCallback<Long>) connection -> connection.del(keyBytes));
    }

    /**
     * 刪除Hash結構內鍵和值
     *
     * @param outerKey  外來鍵
     * @param innerKeys 內鍵
     * @return
     */
    public <O, I> Long hDel(O outerKey, List<I> innerKeys) {
        if (innerKeys == null || innerKeys.isEmpty()) return 0L;
        byte[][] innerKeyBytes = new byte[innerKeys.size()][];
        int index = 0;
        for (I key : innerKeys) {
            innerKeyBytes[index] = JSON.toJSONBytes(key);
            index++;
        }
        return redisTemplate.execute((RedisCallback<Long>) connection ->
                connection.hDel(JSON.toJSONBytes(outerKey), innerKeyBytes));
    }
}

這裡很方便,因為在spring和redis整合後,啟動專案時,spring會自動從application.properties中讀取redis的資訊,並根據這些資訊構建RedisTemplate和StringRedisTemplate物件,交由Spring管理。因此這裡可以直接注入。

RedisClientController.java

在com.springboot.utui.web.controller包下建立一個RedisClientController類,用於測試整合結果。

@Controller
@RequestMapping("/redis")
@Slf4j
public class RedisClientController {

    @Autowired
    private RedisClientUtil redisClient;

    /**
     * 測試鍵值對
     */
    @RequestMapping(method = RequestMethod.GET, value = "/testKv")
    @ResponseBody
    public String testKv() {
        Boolean insertResult = redisClient.set("test-key-1", "test-value-1");
        redisClient.set("test-key-2", "test-value-2");
        redisClient.set("test-key-3", "test-value-3");
        String getResult = redisClient.get("test-key-1", new TypeReference<String>() {
        });
        return "insertResult:" + insertResult + ",getResult:" + getResult;
    }

    /**
     * 測試Hash結構
     */
    @RequestMapping(method = RequestMethod.GET, value = "/testKm")
    @ResponseBody
    public String testKm() {
        Boolean hSetResult = redisClient.hSet("test-hSet-outer", "test-hSet-innerKey", "test-hSet-innerValue");
        Map<String, String> innerMap = new HashMap<>();
        innerMap.put("test-hSetMap-innerKey-1", "test-hSetMap-innerValue-1");
        innerMap.put("test-hSetMap-innerKey-2", "test-hSetMap-innerValue-2");
        innerMap.put("test-hSetMap-innerKey-3", "test-hSetMap-innerValue-3");
        Boolean hSetMapResult = redisClient.hSetMap("test-hSetMap-outer", innerMap);
        String hGetResult = redisClient.hGet("test-hSet-outer", "test-hSet-innerKey", new TypeReference<String>() {
        });
        Map<String, String> hGetMapResult = redisClient.hGetMap("test-hSetMap-outer", new TypeReference<String>() {
        }, new TypeReference<String>() {
        });
        return "hSetResult:" + hSetResult + ",hGetResult:" + hGetResult + ",hSetMapResult:" + hSetMapResult + ",hGetMapResult:" + hGetMapResult;
    }

    /**
     * 測試刪除
     */
    @RequestMapping(method = RequestMethod.GET, value = "/testDel")
    @ResponseBody
    public String testDel() {
        List<String> delList = new ArrayList<>();
        delList.add("test-key-1");
        delList.add("test-key-2");
        Long delNum = redisClient.del(delList);
        String delAfter = redisClient.get("test-key-1", new TypeReference<String>() {
        });
        List<String> hDelList = new ArrayList<>();
        hDelList.add("test-hSetMap-innerKey-1");
        hDelList.add("test-hSetMap-innerKey-2");
        Long hDelNum = redisClient.hDel("test-hSet-outer", hDelList);
        String hDelAfter = redisClient.hGet("test-hSet-outer", "test-hSetMap-innerKey-1", new TypeReference<String>() {
        });
        return "delNum:" + delNum + ",delAfter:" + delAfter + ",hDelNum:" + hDelNum + ",hDelAfter:" + hDelAfter;
    }
}
功能驗證

步驟:
1. 啟動專案
2. 瀏覽器訪問:http://localhost:8080/redis/testKv,響應結果:insertResult:true,getResult:test-value-1
3. 瀏覽器訪問:http://localhost:8080/redis/testKm,響應結果:hSetResult:true,hGetResult:test-hSet-innerValue,hSetMapResult:true,hGetMapResult:{test-hSetMap-innerKey-3=test-hSetMap-innerValue-3, test-hSetMap-innerKey-2=test-hSetMap-innerValue-2, test-hSetMap-innerKey-1=test-hSetMap-innerValue-1}
4. 瀏覽器訪問:http://localhost:8080/redis/testDel,響應結果:delNum:2,delAfter:null,hDelNum:0,hDelAfter:null

單機和叢集

上面的操作步驟都是基於一臺redis伺服器來做的,如果是基於多臺redis服務,也就是叢集,這裡只要將spring.redis.host/spring.redis.port註釋掉,將spring.redis.cluster.nodes註釋放開就可以了,沒有其他什麼操作,就是這麼簡單,其他程式碼都不需要動,上手測試即可。

springboot整合整合jedis方式實現(單機模式)

JedisPoolBootConfig.java

在com.springboot.utui.web下建立config包,建立JedisPoolBootConfig類。

@Configuration
public class JedisPoolBootConfig {
    @Value("${spring.redis.host}")
    private String redisHost;
    @Value("${spring.redis.port}")
    private int redisPort;
    @Value("${spring.redis.max-wait}")
    private Integer maxWait;
    @Value("${spring.redis.pool.max-active}")
    private Integer maxActive;
    @Value("${spring.redis.pool.max-idle}")
    private Integer maxIdle;
    @Value("${spring.redis.pool.min-idle}")
    private Integer minIdle;
    @Value("${spring.redis.timeout}")
    private int timeout;

    @Bean
    public ShardedJedisPool getShardedJedisPool() {
        //Jedis配置資訊
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(maxActive);
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMinIdle(minIdle);
        jedisPoolConfig.setMaxWaitMillis(maxWait);
        JedisShardInfo shardInfo = new JedisShardInfo(redisHost, redisPort, timeout);
        List<JedisShardInfo> shardInfos = new ArrayList<>();
        shardInfos.add(shardInfo);
        return new ShardedJedisPool(jedisPoolConfig, shardInfos);
    }

    @Bean
    public JedisPool getJedisPool() {
        //Jedis配置資訊
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(maxActive);
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMinIdle(minIdle);
        jedisPoolConfig.setMaxWaitMillis(maxWait);
        return new JedisPool(jedisPoolConfig, redisHost, redisPort, timeout);
    }
}
RedisClientController.java

在RedisClientController.java中匯入ShardedJedisPool\JedisPool物件並新增測試方法。

@Autowired
private ShardedJedisPool shardedJedisPool;
@Autowired
private JedisPool jedisPool;
/**
 * 測試JedisShardedJedis
 */
@RequestMapping(method = RequestMethod.GET, value = "/testShardedJedis")
@ResponseBody
public String testShardedJedis() {
    ShardedJedis sharJedis = shardedJedisPool.getResource();
    sharJedis.set("sharJedis-test", "sharJedis-test-value");
    return "sharJedis:" + sharJedis.get("sharJedis-test-value");
}

/**
 * 測試JedisPool
 */
@RequestMapping(method = RequestMethod.GET, value = "/testJedisPool")
@ResponseBody
public String testJedisPool() {
    Jedis jedis = jedisPool.getResource();
    jedis.set("jedis-test", "jedis-test-value");
    return "jedis:" + jedis.get("jedis-test");
}

執行專案測試後得到相應的結果,這裡ShardedJedisPool和JedisPool是兩種實現單機Redis服務的方式,二者去其一即可,這裡作為演示就都寫出來。

springboot整合整合jedis方式實現(叢集模式)

JedisClusterConfig.java

在com.springboot.utui.web.config包中建立JedisClusterConfig類。

@Configuration
public class JedisClusterConfig {
    @Value("${spring.redis.cluster.nodes}")
    private String redisNodes;
    @Value("${spring.redis.max-wait}")
    private Integer maxWait;
    @Value("${spring.redis.pool.max-idle}")
    private Integer maxIdle;
    @Value("${spring.redis.pool.min-idle}")
    private Integer minIdle;
    @Value("${spring.redis.timeout}")
    private int timeout;

    @Bean
    public JedisCluster getJedisCluster() {
        String[] cNodes = redisNodes.split(",");
        Set<HostAndPort> nodes = new HashSet<>();
        for (String cNode : cNodes) {
            String[] hp = cNode.split(":");
            nodes.add(new HostAndPort(hp[0], Integer.valueOf(hp[1])));
        }
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxWaitMillis(maxWait);
        poolConfig.setMaxIdle(maxIdle);
        poolConfig.setMinIdle(minIdle);
        return new JedisCluster(nodes, timeout, poolConfig);
    }
}
RedisClientController.java

在RedisClientController.java中匯入JedisCluster物件並新增測試方法。

@Autowired
private JedisCluster jedisCluster;
/**
 * 測試JedisCluster
 */
@RequestMapping(method = RequestMethod.GET, value = "/testJedisCluster")
@ResponseBody
public String testJedisCluster() {
    jedisCluster.set("jedisCluster-test", "jedisCluster-test-value");
    return "jedisCluster:" + jedisCluster.get("jedisCluster-test");
}

執行專案測試瀏覽器訪問得到對應結果。

spring boot構建基礎版web專案系列:

相關推薦

spring boot構建基礎web專案()-springbootredis資料快取整合

寫前說明 在spring+spring mvc+mybatis模式下,使用的最多的就是jedis,但是spring boot整合了redis後,依然可以使用jedis,但是同時也提供了一個RedisTemplate和StringRedisTemplat

spring boot構建基礎web專案(一)springbootthymeleaf控制層基礎構建

寫前說明 根據個人在各篇博文中看到的資訊,以及自己的總結,下面的說明文件只涉及到簡明的一些知識點。如thymeleaf的檔案路徑配置、快取等,都是直接使用預設的方式,但是實際上可以通過程式碼中載入和配置檔案配置的方式個性化,這裡不做詳細的說明,因為在實

建立一個Spring Boot+Mybatis+Oracle的Web專案

最近學習Spring Boot,剛好複習下Mybatis,因為現在用的是Hibernate,Mybatis已經有兩年沒用過了。 首先我用的資料庫是Oracle. 1、New Project  2、建立完成後的目錄結構如下:(如果有的同學的資料庫使用的是mysql的話

Spring Boot構建Web專案如何在服務端校驗表單輸入

本文首發於個人網站:Spring Boot構建的Web專案如何在服務端校驗表單輸入 這個例子用於演示在Spring Boot應用中如何驗證Web 應用的輸入,我們將會建立一個簡單的Spring MVC應用,來讀取使用者輸入並使用validation註解來檢查,並且當用戶輸入錯誤時,應用需要再螢幕上顯示錯誤

Spring Boot基礎篇):建立一個簡單的SpringBoot專案

Spring Boot建立   首先IDEA新建一個專案,File-New-Project-Spring Initializr,然後Next,如下圖: 填寫專案資訊: 填寫專案使用到的技術,上面的SpringBoot版本建議選擇最新的穩定版,下面勾選上Web就可

Spring Boot + Spring Cloud 構建微服務系統():服務消費和負載(Feign)

Spring Cloud Feign Spring Cloud Feign是一套基於Netflix Feign實現的宣告式服務呼叫客戶端。它使得編寫Web服務客戶端變得更加簡單。我們只需要通過建立介面並用註解來配置它既可完成對Web服務介面的繫結。它具備可插拔的註解支援,包括Feign註解、JAX-RS註解

spring-boot構建RESTful Web服務

往後官方demo的筆記不貼程式碼了,只貼連結 官方文件 RESTful Web服務 該服務將處理GET請求/greeting,可選地使用name查詢字串中的引數。該GET請求應該返回200 OK在表示問候的身體與JSON響應。它應該看起來像這樣: { "id": 1, "content

spring boot構建微服務-通過maven構建專案HelloWorld

1.訪問如下地址來構建基礎專案 地址:http://start.spring.io/ 2.具體操作選項如下 a.Generate a:建立一個什麼樣的工程,這裡選擇Maven Project b.with:通過什麼語言,這裡選擇Java c.and Spring Boot:這個是選擇sp

Spring Boot 構建restful web服務

1.建立依賴檔案 檔名:pom.xml 檔案內容: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://ww

Spring之使用Spring Boot構建RESTful Web服務

英文原文:https://spring.io/guides/gs/rest-service/ 目錄 你要構建什麼 你需要什麼 如何完成本指南 建立資源表示類 建立資源控制器 使應用程式可執行 構建可執行的JAR 測試服務 總結 參考 本指南將引導您

Spring Boot 2.0 的開源專案雲收藏來了!

給大家聊一聊雲收藏從 Spring Boot 1.0 升級到 2.0 所踩的坑 先給大家晒一下雲收藏的幾個資料,作為一個 Spring Boot 的開源專案(https://github.com/cloudfavorites/favorites-web)目前在 Github 上面已經有1600多個 Star,

Spring Boot 構建企業級部落格學習(

ide外掛的安裝配置、專案執行 安裝、配置ide 常用外掛安裝、配置 匯入專案到ide 啟動gradle的方式 eclipse環境配置 jdk配置 在eslipse中window-preferences-java裡配置jdk 安裝gradle

Spring Boot 構建電商基礎秒殺項目 (九) 商品列表 & 詳情

des img getitem strip 技術 ng- omap label v-for SpringBoot構建電商基礎秒殺項目 學習筆記 ItemDOMapper.xml 添加 <select id="listItem" result

Spring Boot 構建電商基礎秒殺項目 (十) 交易下單

ttr sum dell tps 項目 true create items log SpringBoot構建電商基礎秒殺項目 學習筆記 新建表 create table if not exists order_info ( id varchar(32) not

Spring Boot 構建電商基礎秒殺項目 (十一) 秒殺

boolean exception amount mar () default from copy param SpringBoot構建電商基礎秒殺項目 學習筆記 新建表 create table if not exists promo ( id int not

Spring Boot2 系列教程(十一)Spring Boot 構建 RESTful 風格應用

RESTful ,到現在相信已經沒人不知道這個東西了吧!關於 RESTful 的概念,我這裡就不做過多介紹了,傳統的 Struts 對 RESTful 支援不夠友好 ,但是 SpringMVC 對於 RESTful 提供了很好的支援,常見的相關注解有: @RestController @GetMapping

spring boot 登錄註冊 demo () -- 前後端傳遞

lin 表單提交 www col log ref rec put 內容 前端頁面通過thymeleaf渲染     <dependency> <groupId>org.springframework.boot</gro

【Linux系列】【基礎】第章 Shell基礎知識

linux shell 3. Shell基礎知識 3.1 Shell的由來 3.1.1 Bourne Agin Shell 3.1.2 其他的shell, 有 zsh, ksh等 3.2 查看有沒有shell 3.2.1 yum list

spring Bootspring boot獲取資源文件的種方式【兩種情況下】

不用 ash 12c ica pla bsp foreach set 使用 首先聲明一點,springboot獲取資源文件,需要看是   1》從spring boot默認的application.properties資源文件中獲取   2》還是從自定義的資源文件中獲取

Spring boot(二)—Web開發

final 一次 org init 調用 mat 重要 映射 ppi 上篇文章介紹了Spring boot初級教程:spring boot(一):入門篇,方便大家快速入門、了解實踐Spring boot特性;本篇文章接著上篇內容繼續為大家介紹spring boot的其它特性