1.springcloud微服务架构搭建 之 《springboot自动装配Redis》

背景:由于自研框架,好多公司都自己封装自己的Redis,所以需要单独讲redis封装起来,作为公举模块给业务系统用。自动装配原理就不介绍了,网上一搜一大堆,说的都不错。废话不多数,上代码

目录

项目结构:

1.springboot 版本号选择

 2.项目根pom配置 lilock-framework

3.公共模块pom配置 lilock-commons

4.redis模块pom配置  lilock-redis-spring-boot-starter

        4.1 自定义redis参数配置类

        4.2 自定义redis工具类 RedisService

        4.3 自定义redis配置类

        4.4 springboot装配文件 spring.factories

5.新建lilock-server-user模块 

        5.1 application.yml配置redis参数

         5.2 新建测试类

        5.3 测试结果


项目结构:

lilock-framework
    lilock-commons
        lilock-common-spring-boot-starter
        lilock-redis-spring-boot-starter
    lilock-modules
        lilock-service-user

1.springboot 版本号选择

我自己选定的版本号是 2.3.12.RELEASE,可以根据自己公司对springboot的版本号情况自己选定

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.12.RELEASE</version> 
    <relativePath/> <!-- lookup parent from repository -->
</parent>

 2.项目根pom配置 lilock-framework

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <modules>
        <module>lilock-commons</module>
        <module>lilock-modules</module>

    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
    </parent>
    <groupId>lilock.cn</groupId>
    <artifactId>lilock-framework</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>lilock-framework</name>
    <packaging>pom</packaging>
    <properties>
        <java.version>8</java.version>
        <spring.boot.version>2.3.12.RELEASE</spring.boot.version>
        <lombok.version>1.18.26</lombok.version>
        <jackson.version>2.14.2</jackson.version>
        <jedis.version>3.8.0</jedis.version>
        <swagger.version>2.9.2</swagger.version>
        <fastjson.version>1.2.70</fastjson.version>
        <nacos.version>2.1.0.RELEASE</nacos.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>${spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <version>${spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-autoconfigure</artifactId>
                <version>${spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
                <version>${spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <version>${spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>${swagger.version}</version>
            </dependency>
            <!-- fastjson -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>${swagger.version}</version>
            </dependency>
            <!-- jedis配置-->
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>${jedis.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <!-- Jackson序列化配置-->
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-core</artifactId>
                <version>${jackson.version}</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-annotations</artifactId>
                <version>${jackson.version}</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>${jackson.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3.公共模块pom配置 lilock-commons

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>lilock-framework</artifactId>
        <groupId>lilock.cn</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>lilock-commons</artifactId>
    <packaging>pom</packaging>
    <modules>
        <module>lilock-redis-spring-boot-starter</module>
    </modules>

</project>

4.redis模块pom配置  lilock-redis-spring-boot-starter

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>lilock-commons</artifactId>
        <groupId>lilock.cn</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath/>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>lilock-common-spring-redis-starter</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <!-- 以防止repackage目标将依赖项添加到 jar 中 -->
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-configuration-processor</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

上述配置Redis自动装配模块的所有配置,开始进入主题

        4.1 自定义redis参数配置类

package lilock.cn.common.redis.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@ConfigurationProperties(prefix = "lilock.redis")
public class RedisDataSourceProperties {

    //链接地址
    private String host;

    //端口号
    private Integer port;

    //默认database
    private Integer database;

    //密码
    private String password;

    //超时时间
    private Integer timeout;

    //最大空闲数
    private Integer maxIdle;

    //连接池最大连接数
    private Integer maxTotal;

    //最大等待时间
    private Integer maxWaitMillis;

    //逐出链接最小空闲时间
    private Integer minEvictableIdleTimeMillis;

    //每次最大逐出数量
    private Integer numTestsPerEvictionRun;

    //逐出扫描时间的间隔
    private Long timeBetweenEvictionRunsMillis;

    ///是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
    private boolean testOnBorrow;

    //空闲时检查有效性
    private boolean testWhileIdle;
}

        4.2 自定义redis工具类 RedisService

package lilock.cn.common.redis.template;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.RedisClusterNode;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisServerCommands;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.CollectionUtils;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Redis Repository
 * redis 基本操作 可扩展,基本够用了
 *
 */
@Slf4j
public class RedisService {
    /**
     * 默认编码
     */
    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

    /**
     * key序列化
     */
    private static final StringRedisSerializer STRING_SERIALIZER = new StringRedisSerializer();

    /**
     * value 序列化
     */
    private static final JdkSerializationRedisSerializer OBJECT_SERIALIZER = new JdkSerializationRedisSerializer();

    /**
     * Spring Redis Template
     */
    private RedisTemplate<String, Object> redisTemplate;




    public RedisService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.redisTemplate.setKeySerializer(STRING_SERIALIZER);
        this.redisTemplate.setValueSerializer(OBJECT_SERIALIZER);
    }

    /**
     * 获取链接工厂
     */
    public RedisConnectionFactory getConnectionFactory() {
        return this.redisTemplate.getConnectionFactory();

    }

    /**
     * 获取 RedisTemplate对象
     */
    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    /**
     * 清空DB
     *
     * @param node redis 节点
     */
    public void flushDB(RedisClusterNode node) {
        this.redisTemplate.opsForCluster().flushDb(node);
    }

    /**
     * 添加到带有 过期时间的  缓存
     *
     * @param key   redis主键
     * @param value 值
     * @param time  过期时间(单位秒)
     */
    public void setExpire(final byte[] key, final byte[] value, final long time) {
        redisTemplate.execute((RedisCallback<Long>) connection -> {
            connection.setEx(key, time, value);
            log.debug("[redisTemplate redis]放入 缓存  url:{} ========缓存时间为{}秒", key, time);
            return 1L;
        });
    }

    /**
     * 添加到带有 过期时间的  缓存
     *
     * @param key   redis主键
     * @param value 值
     * @param time  过期时间(单位秒)
     */
    public void setExpire(final String key, final Object value, final long time) {
        redisTemplate.execute((RedisCallback<Long>) connection -> {
            RedisSerializer<String> serializer = getRedisSerializer();
            byte[] keys = serializer.serialize(key);
            byte[] values = OBJECT_SERIALIZER.serialize(value);
            boolean f = connection.setEx(keys, time, values);
            log.info("[redisTemplate redis]放入 缓存  url:{} ========缓存时间为{}秒,{}", key, time, f);
            return 1L;
        });
    }

    /**
     * 一次性添加数组到   过期时间的  缓存,不用多次连接,节省开销
     *
     * @param keys   redis主键数组
     * @param values 值数组
     * @param time   过期时间(单位秒)
     */
    public void setExpire(final String[] keys, final Object[] values, final long time) {
        redisTemplate.execute((RedisCallback<Long>) connection -> {
            RedisSerializer<String> serializer = getRedisSerializer();
            for (int i = 0; i < keys.length; i++) {
                byte[] bKeys = serializer.serialize(keys[i]);
                byte[] bValues = OBJECT_SERIALIZER.serialize(values[i]);
                connection.setEx(bKeys, time, bValues);
            }
            return 1L;
        });
    }


    /**
     * 一次性添加数组到   过期时间的  缓存,不用多次连接,节省开销
     *
     * @param keys   the keys
     * @param values the values
     */
    public void set(final String[] keys, final Object[] values) {
        redisTemplate.execute((RedisCallback<Long>) connection -> {
            RedisSerializer<String> serializer = getRedisSerializer();
            for (int i = 0; i < keys.length; i++) {
                byte[] bKeys = serializer.serialize(keys[i]);
                byte[] bValues = OBJECT_SERIALIZER.serialize(values[i]);
                connection.set(bKeys, bValues);
            }
            return 1L;
        });
    }


    /**
     * 添加到缓存
     *
     * @param key   the key
     * @param value the value
     */
    public void set(final String key, final Object value) {
        redisTemplate.execute((RedisCallback<Long>) connection -> {
            RedisSerializer<String> serializer = getRedisSerializer();
            byte[] keys = serializer.serialize(key);
            byte[] values = OBJECT_SERIALIZER.serialize(value);
            connection.set(keys, values);
            log.debug("[redisTemplate redis]放入 缓存  url:{}", key);
            return 1L;
        });
    }

    /**
     * 查询在这个时间段内即将过期的key
     *
     * @param key  the key
     * @param time the time
     * @return the list
     */
    public List<String> willExpire(final String key, final long time) {
        final List<String> keysList = new ArrayList<>();
        redisTemplate.execute((RedisCallback<List<String>>) connection -> {
            Set<String> keys = redisTemplate.keys(key + "*");
            for (String key1 : keys) {
                Long ttl = connection.ttl(key1.getBytes(DEFAULT_CHARSET));
                if (0 <= ttl && ttl <= 2 * time) {
                    keysList.add(key1);
                }
            }
            return keysList;
        });
        return keysList;
    }


    /**
     * 查询在以keyPatten的所有  key
     *
     * @param keyPatten the key patten
     * @return the set
     */
    public Set<String> keys(final String keyPatten) {
        return redisTemplate.execute((RedisCallback<Set<String>>) connection -> redisTemplate.keys(keyPatten + "*"));
    }
    
    /**
     * 利用scan 查询 keyPattern关键字的key集合
     * @param keyPattern
     * @return
     */
    public Set<String> scan(final String keyPattern) {
        Set<String> keys = redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
            Set<String> keysTmp = new HashSet<>();
            Cursor<byte[]> cursor = connection.scan(KeyScanOptions.scanOptions().match( keyPattern + "*").count(1000).build());
            while (cursor.hasNext()) {
                keysTmp.add(new String(cursor.next()));
            }
            return keysTmp;
        });
        return keys;
    }

    /**
     * 根据key获取对象
     *
     * @param key the key
     * @return the byte [ ]
     */
    public byte[] get(final byte[] key) {
        byte[] result = redisTemplate.execute((RedisCallback<byte[]>) connection -> connection.get(key));
        log.debug("[redisTemplate redis]取出 缓存  url:{} ", key);
        return result;
    }

    /**
     * 根据key获取对象
     *
     * @param key the key
     * @return the string
     */
    public Object get(final String key) {
        Object resultStr = redisTemplate.execute((RedisCallback<Object>) connection -> {
            RedisSerializer<String> serializer = getRedisSerializer();
            byte[] keys = serializer.serialize(key);
            byte[] values = connection.get(keys);
            return OBJECT_SERIALIZER.deserialize(values);
        });
        log.debug("[redisTemplate redis]取出 缓存  url:{} ", key);
        return resultStr;
    }


    /**
     * 根据key获取对象
     *
     * @param keyPatten the key patten
     * @return the keys values
     */
    public Map<String, Object> getKeysValues(final String keyPatten) {
        log.debug("[redisTemplate redis]  getValues()  patten={} ", keyPatten);
        return redisTemplate.execute((RedisCallback<Map<String, Object>>) connection -> {
            RedisSerializer<String> serializer = getRedisSerializer();
            Map<String, Object> maps = new HashMap<>(16);
            Set<String> keys = redisTemplate.keys(keyPatten + "*");
            if (!CollectionUtils.isEmpty(keys)) {
                for (String key : keys) {
                    byte[] bKeys = serializer.serialize(key);
                    byte[] bValues = connection.get(bKeys);
                    Object value = OBJECT_SERIALIZER.deserialize(bValues);
                    maps.put(key, value);
                }
            }
            return maps;
        });
    }

    /**
     * Ops for hash hash operations.
     *
     * @return the hash operations
     */
    public HashOperations<String, String, Object> opsForHash() {
        return redisTemplate.opsForHash();
    }

    /**
     * 对HashMap操作
     *
     * @param key       the key
     * @param hashKey   the hash key
     * @param hashValue the hash value
     */
    public void putHashValue(String key, String hashKey, Object hashValue) {
        log.debug("[redisTemplate redis]  putHashValue()  key={},hashKey={},hashValue={} ", key, hashKey, hashValue);
        opsForHash().put(key, hashKey, hashValue);
    }

    /**
     * 获取单个field对应的值
     *
     * @param key     the key
     * @param hashKey the hash key
     * @return the hash values
     */
    public Object getHashValues(String key, String hashKey) {
        log.debug("[redisTemplate redis]  getHashValues()  key={},hashKey={}", key, hashKey);
        return opsForHash().get(key, hashKey);
    }

    /**
     * 根据key值删除
     *
     * @param key      the key
     * @param hashKeys the hash keys
     */
    public void delHashValues(String key, Object... hashKeys) {
        log.debug("[redisTemplate redis]  delHashValues()  key={}", key);
        opsForHash().delete(key, hashKeys);
    }

    /**
     * key只匹配map
     *
     * @param key the key
     * @return the hash value
     */
    public Map<String, Object> getHashValue(String key) {
        log.debug("[redisTemplate redis]  getHashValue()  key={}", key);
        return opsForHash().entries(key);
    }

    /**
     * 批量添加
     *
     * @param key the key
     * @param map the map
     */
    public void putHashValues(String key, Map<String, Object> map) {
        opsForHash().putAll(key, map);
    }

    /**
     * 集合数量
     *
     * @return the long
     */
    public long dbSize() {
        return redisTemplate.execute(RedisServerCommands::dbSize);
    }

    /**
     * 清空redis存储的数据
     *
     * @return the string
     */
    public String flushDB() {
        return redisTemplate.execute((RedisCallback<String>) connection -> {
            connection.flushDb();
            return "ok";
        });
    }

    /**
     * 判断某个主键是否存在
     *
     * @param key the key
     * @return the boolean
     */
    public boolean exists(final String key) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> connection.exists(key.getBytes(DEFAULT_CHARSET)));
    }


    /**
     * 删除key
     *
     * @param keys the keys
     * @return the long
     */
    public long del(final String... keys) {
        return redisTemplate.execute((RedisCallback<Long>) connection -> {
            long result = 0;
            for (String key : keys) {
                result = connection.del(key.getBytes(DEFAULT_CHARSET));
            }
            return result;
        });
    }

    /**
     * 获取 RedisSerializer
     *
     * @return the redis serializer
     */
    protected RedisSerializer<String> getRedisSerializer() {
        return redisTemplate.getStringSerializer();
    }

    /**
     * 对某个主键对应的值加一,value值必须是全数字的字符串
     *
     * @param key the key
     * @return the long
     */
    public long incr(final String key) {
        return redisTemplate.execute((RedisCallback<Long>) connection -> {
            RedisSerializer<String> redisSerializer = getRedisSerializer();
            return connection.incr(redisSerializer.serialize(key));
        });
    }
    
    /**
     * 对某个主键对应的值减一,value值必须是全数字的字符串
     *
     * @param key the key
     * @return the long
     */
    public long decr(final String key) {
        return redisTemplate.execute((RedisCallback<Long>) connection -> {
            RedisSerializer<String> redisSerializer = getRedisSerializer();
            return connection.decr(redisSerializer.serialize(key));
        });
    }

    /**
     * redis List 引擎
     *
     * @return the list operations
     */
    public ListOperations<String, Object> opsForList() {
        return redisTemplate.opsForList();
    }

    /**
     * redis set
     * @return
     */
    public SetOperations<String, Object> opsForSet(){
        return redisTemplate.opsForSet();
    }

    /**
     * //集合中添加元素,返回添加个数
     * @param setkeyName
     * @param value
     * @return
     */
    public Long addToSet(String setkeyName, String... value){
        Long addNum = redisTemplate.opsForSet().add(setkeyName, value);
        return addNum;
    }

    /**
     * 获取集合大小
     * @param setKeyName
     * @return
     */
    public Long getSetSize(String setKeyName){
        return redisTemplate.opsForSet().size(setKeyName);
    }

    /**
     * 获取所有的元素
     * @param setKeyName
     * @return
     */
    public Set getSetMember(String setKeyName){
        Set<Object> members = redisTemplate.opsForSet().members(setKeyName);
        return members;
    }

    /**
     * 从集合中删除指定元素
     * @param setkeyName
     * @param value
     * @return
     */
    public Long removeFromSet(String setkeyName, String value){
        Long removeNum = redisTemplate.opsForSet().remove(setkeyName, value);
        return removeNum;
    }

    /**
     * //判断集合中是否存在某元素
     * @param setkeyName
     * @param value
     * @return
     */
    public boolean isHaveFromSet(String setkeyName, String value){
        return redisTemplate.opsForSet().isMember(setkeyName, value);
    }

    /**
     * redis List数据结构 : 将一个或多个值 value 插入到列表 key 的表头
     *
     * @param key   the key
     * @param value the value
     * @return the long
     */
    public Long leftPush(String key, Object value) {
        return opsForList().leftPush(key, value);
    }

    /**
     * redis List数据结构 : 移除并返回列表 key 的头元素
     *
     * @param key the key
     * @return the string
     */
    public Object leftPop(String key) {
        return opsForList().leftPop(key);
    }

    /**
     * redis List数据结构 :将一个或多个值 value 插入到列表 key 的表尾(最右边)。
     *
     * @param key   the key
     * @param value the value
     * @return the long
     */
    public Long in(String key, Object value) {
        return opsForList().rightPush(key, value);
    }

    /**
     * redis List数据结构 : 移除并返回列表 key 的末尾元素
     *
     * @param key the key
     * @return the string
     */
    public Object rightPop(String key) {
        return opsForList().rightPop(key);
    }


    /**
     * redis List数据结构 : 返回列表 key 的长度 ; 如果 key 不存在,则 key 被解释为一个空列表,返回 0 ; 如果 key 不是列表类型,返回一个错误。
     *
     * @param key the key
     * @return the long
     */
    public Long length(String key) {
        return opsForList().size(key);
    }


    /**
     * redis List数据结构 : 根据参数 i 的值,移除列表中与参数 value 相等的元素
     *
     * @param key   the key
     * @param i     the
     * @param value the value
     */
    public void remove(String key, long i, Object value) {
        opsForList().remove(key, i, value);
    }

    /**
     * redis List数据结构 : 将列表 key 下标为 index 的元素的值设置为 value
     *
     * @param key   the key
     * @param index the index
     * @param value the value
     */
    public void set(String key, long index, Object value) {
        opsForList().set(key, index, value);
    }

    /**
     * redis List数据结构 : 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 end 指定。
     *
     * @param key   the key
     * @param start the start
     * @param end   the end
     * @return the list
     */
    public List<Object> getList(String key, int start, int end) {
        return opsForList().range(key, start, end);
    }

    /**
     * redis List数据结构 : 批量存储
     *
     * @param key  the key
     * @param list the list
     * @return the long
     */
    public Long leftPushAll(String key, List<String> list) {
        return opsForList().leftPushAll(key, list);
    }

    /***
     * redis List数据结构:批量存储列表信息
     * @param key
     * @param list
     * @return
     */
    public Long rightPushAllObj(String key, List<Object> list) {
        return opsForList().rightPushAll(key,list);
    }

    /**
     * redis List数据结构 : 将值 value 插入到列表 key 当中,位于值 index 之前或之后,默认之后。
     *
     * @param key   the key
     * @param index the index
     * @param value the value
     */
    public void insert(String key, long index, Object value) {
        opsForList().set(key, index, value);
    }


    /**
     * 设置缓存
     *
     * @param key   key
     * @param value value 存储结果是string,不是16进制
     * @param time  time大于0,则设置过期时间,否则不设置过期时间
     * @return
     */
    public boolean set(String key, String value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                redisTemplate.opsForValue().set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取key对应的value  value 为String
     *
     * @param key key
     * @return
     */
    public String getStrValue(String key) {
        if(key == null){
            return null;
        }
        Object value = redisTemplate.opsForValue().get(key);
        if(null != value){
            return (String)value;
        }else{
            return null;
        }
    }

    /***
     * 设置key 的过期时间
     * @param key
     * @param time
     * @param unit
     */
    public void expireAt(String key,Long time,TimeUnit unit){
        redisTemplate.expire(key,time,unit);
    }

    /**
     * 设置位图
     * @param key key
     * @param offSet 偏移量
     * @param flag true为1 false为0
     */
    public void setBit(String key, Long offSet, boolean flag) {
        redisTemplate.opsForValue().setBit(key, offSet, flag);
    }

    /**
     * 获取位图
     * @param key key
     * @param offSet 偏移量
     */
    public boolean getBit(String key, Long offSet) {
        return redisTemplate.opsForValue().getBit(key, offSet);
    }

    /**
     * 获取指定key的value为1的数量
     * @param key
     * @return
     */
    public Long bitCount(String key) {
        return redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes()));
    }

    /**
     * 获取key的过期时间
     * @param key
     * @return
     */
    public Long getExpire(String key) {
        return redisTemplate.opsForValue().getOperations().getExpire(key);
    }

    /**
     *
     * @param completeKey
     * @param bitmapKey
     * @param value
     * @return
     */
    public Long setLoginByLua(String completeKey, String bitmapKey, String value, Integer loginCount) {
        DefaultRedisScript redisScript = new DefaultRedisScript();
        redisScript.setLocation(new ClassPathResource("redisSetLogin.lua"));
        redisScript.setResultType(String.class);

        //logincount转为string是因为StringRedisSerializer
        String result = redisTemplate.execute(redisScript, new StringRedisSerializer(), new StringRedisSerializer(),
                Arrays.asList(completeKey, bitmapKey), value, String.valueOf(loginCount));
        Long count = bitCount(bitmapKey);
        log.info("登录统计,KEY:{},人数:{}",bitmapKey,count);
        return Long.valueOf(result);
    }

    /**
     * 设置位图偏移量的value为0
     * @param bitmapKey 位图key
     * @param offset 偏移量
     */
    public void setBitmap(String bitmapKey, String offset) {
        DefaultRedisScript redisScript = new DefaultRedisScript();
        redisScript.setLocation(new ClassPathResource("redisSetBit.lua"));
        redisScript.setResultType(Long.class);
        redisTemplate.execute(redisScript, new StringRedisSerializer(), new StringRedisSerializer(),
                Arrays.asList(bitmapKey), offset);
    }
}

        4.3 自定义redis配置类

package lilock.cn.common.redis.config;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import lilock.cn.common.redis.properties.RedisDataSourceProperties;
import lilock.cn.common.redis.template.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;
import redis.clients.jedis.JedisPoolConfig;

import java.lang.reflect.Method;
import java.time.Duration;


/**
 * redis 配置流程
 * 1.通过yaml 或者properties配置文件获取对应的redis的相关参数
 * 2.利用redis相关参数配置连接池
 * 2.利用连接池配置链接工厂
 * 3.利用链接工厂设置Redistemplate,设置key 和 value的序列化方式
 * 4.封装redis工具,利用redistemplate跟redis交互
 */
@EnableConfigurationProperties
@EnableCaching //开启SpringCache 管理
@Slf4j
public class RedisConfig extends CachingConfigurerSupport {
    @Autowired
    private RedisDataSourceProperties redisDataSourceProperties;
    /**
     * 配置key的生成策略
     * @return
     */
    @Bean
    public KeyGenerator keyGenerator(){
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(":");
                sb.append(method.getName());
                sb.append(":");
                for (Object param : params) {
                    sb.append(param.toString());
                }
                return sb.toString();
            }
        };
    }


    /**
     * 配置jedis连接池
     * @return
     */
    @Bean("jedisPool")
    @Primary
    public JedisPoolConfig jedisPoolConfig(){
        //创建链接池
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        //获取最大空闲数
        jedisPoolConfig.setMaxIdle(redisDataSourceProperties.getMaxIdle());
        //连接池最大连接数量
        jedisPoolConfig.setMaxTotal(redisDataSourceProperties.getMaxTotal());
        //连接池最大等待时间
        //jedisPoolConfig.setMaxWait(Duration.ofSeconds(redisDataSourceProperties.getMaxTotal()));
        //逐出链接最小空闲时间
        //jedisPoolConfig.setMinEvictableIdleTime(Duration.ofSeconds(redisDataSourceProperties.getMinEvictableIdleTimeMillis()));
        //每次逐出最大数量
        jedisPoolConfig.setNumTestsPerEvictionRun(redisDataSourceProperties.getNumTestsPerEvictionRun());
        //逐出扫描时间间隔
        //jedisPoolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(redisDataSourceProperties.getTimeBetweenEvictionRunsMillis()));
        //是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
        jedisPoolConfig.setTestOnBorrow(redisDataSourceProperties.isTestOnBorrow());
        //空闲检查失效性
        jedisPoolConfig.setTestWhileIdle(redisDataSourceProperties.isTestWhileIdle());
        log.info("项目启动加载Redis连接池信息:{}", JSONObject.toJSONString(redisDataSourceProperties));
        return jedisPoolConfig;
    }

    /**
     * 初始化RedisTemplate的配置,配置序列化and工厂
     * @param jedisPoolConfig
     * @return
     */
    @Bean("jedisConnectionFactory")
    @Primary
    public JedisConnectionFactory  jedisConnectionFactory(JedisPoolConfig jedisPoolConfig){
        //配置redis链接信息
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(redisDataSourceProperties.getHost(),redisDataSourceProperties.getPort());
        //设置密码
        redisStandaloneConfiguration.setPassword(RedisPassword.of(redisDataSourceProperties.getPassword()));
        //设置默认database
        redisStandaloneConfiguration.setDatabase(redisDataSourceProperties.getDatabase());
        JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfigurationBuilder = JedisClientConfiguration.builder();
        //设置超时时间
        jedisClientConfigurationBuilder.connectTimeout(Duration.ofSeconds(redisDataSourceProperties.getTimeout()));
        //设置链接池
        jedisClientConfigurationBuilder.usePooling().poolConfig(jedisPoolConfig);
        //创建工厂对象
        JedisConnectionFactory factory = new JedisConnectionFactory(redisStandaloneConfiguration,jedisClientConfigurationBuilder.build());
        return factory;
    }
    /**
     * 配置redis序列化方式
     * @param factory
     * @return
     */
    @Bean("redisTemplate")
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
        //设置链接工厂
        redisTemplate.setConnectionFactory(factory);
        RedisSerializer keySerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会抛出异常
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        // key采用String的序列化方式
        redisTemplate.setKeySerializer(keySerializer);
        // hashkey采用String的序列化方式
        redisTemplate.setHashKeySerializer(keySerializer);

        // value序列化方式采用jackson
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // value序列化方式采用jackson
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    @Primary //@Primary: 当出现相同名字的bean是,优先使用使用了 @Primary 注解的bean
    public RedisService redisUtil(@Qualifier("redisTemplate") RedisTemplate<String,Object> redisTemplate){
        RedisService redisUtil = new RedisService(redisTemplate);
        return redisUtil;
    }


    /**
     * 设置SpringCacheManager ,通过在类名启用@EnableCaching 来开启springcache
     * 配置完此bean之后,可以通过@Cacheable/@CachePut/@CacheEvict 来进行操作缓存
     * @Cacheable   : 根据key从缓存中取值,存在则获取后直接返回,不执行业务方法;不存在则执行业务方法,讲业务方法返回的数据存入缓存
     * @CachePut    : 根据key从缓存中取值,无论数据是否存在,业务方法都会执行,并且业务方法返回的数据存入内存
     * @CacheEvict  : 执行业务方法之后,删除缓存中的数据
     * @param factory
     * @return
     */
    @Bean("cacheManager")
    @Primary
    public CacheManager cacheManager(RedisConnectionFactory factory){
        RedisCacheConfiguration redisAutoConfiguration = RedisCacheConfiguration
                .defaultCacheConfig() //获取redisCache实例
                .entryTtl(Duration.ofHours(1L)) //设置缓存过期时间
                .computePrefixWith(cacheName -> "cache".concat(":").concat(cacheName).concat(":")) //给key值拼接前缀 cache: 类似于传入 key 最后存的时候 是 cache:key
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) //设置key序列化方式
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) //设置value序列化方式
                ;

        RedisCacheManager redisCacheManager = RedisCacheManager
                .builder(factory) //获取redisCacheManager
                .cacheDefaults(redisAutoConfiguration)
                .build();
        return redisCacheManager;
    }

}

        4.4 springboot装配文件 spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
lilock.cn.common.redis.properties.RedisDataSourceProperties,\
lilock.cn.common.redis.config.RedisConfig

 到目前位置,redis 自动装配已经完成,接下来就是测试了

5.新建lilock-server-user模块 

引入 lilock-common-spring-redis-starter
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>lilock-modules</artifactId>
        <groupId>lilock.cn</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath/>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <version>1.0-SNAPSHOT</version>
    <artifactId>lilock-service-user</artifactId>
    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
       <dependency>
            <groupId>lilock.cn</groupId>
            <artifactId>lilock-common-spring-redis-starter</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

        5.1 application.yml配置redis参数

lilock:
  redis:
    host: 127.0.0.1
    port: 6379
    max-idle: 10 #设置最大空闲连接数,默认为8
    max-wait-millis: -1 #获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
    max-total: 20 #设置最大连接数,默认18个
    min-evictable-idle-time-millis: 18000 #设置连接最小的逐出间隔时间,默认1800000毫秒
    num-tests-per-eviction-run: 5 #每次逐出检查时,逐出连接的个数 默认为3
    time-between-eviction-runs-millis: -1 #设置连接对象有效性扫描间隔,设置为-1,则不运行逐出线程
    timeout: 10 #链接超时时间默认2秒
    test-on-borrow: true # 在连接对象返回时,是否测试对象的有效性,默认false
    test-while-idle: true # 在连接池空闲时是否测试连接对象的有效性,默认false
    database: 0
    password:
server:
  port: 8988
spring:
  profiles:
    active: dev

         5.2 新建测试类

package lilock.cn.user.controller;

import io.swagger.annotations.Api;
import lilock.cn.common.redis.template.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(path = "/user")
@Api(value = "系统用户",tags = {"系统用户"})
@Slf4j
public class UserController {

    @Autowired
    private RedisService redisService;

    @GetMapping("/test")
    public String getUser(){
        log.info("{}",redisService);
        redisService.setExpire("zs","张三",1000);
        String value = redisService.getStrValue("zs");
        return value;
    }

}

        5.3 测试结果

搞定!

猜你喜欢

转载自blog.csdn.net/u011291990/article/details/129520991