spring boot構建基礎版web項目(三)-springboot、redis數據緩存整合

原文作者:彌諾R
原文地址:http://www.minuor.com/1523882147/article
轉載聲明:轉載請註明原文地址,注意版權維護,謝謝!

寫前說明

在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項目系列:
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章