bilingfeng 2 лет назад
Родитель
Сommit
6c4033a6c4

+ 42 - 5
game-module/game-common-redis/pom.xml

@@ -8,12 +8,49 @@
         <version>0.0.1-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-
     <artifactId>game-common-redis</artifactId>
+    <description>redis相关</description>
 
-    <properties>
-        <maven.compiler.source>18</maven.compiler.source>
-        <maven.compiler.target>18</maven.compiler.target>
-    </properties>
+    <dependencies>
+        <!-- SpringBoot Boot Redis -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <!--缓存支持-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-cache</artifactId>
+        </dependency>
+        <!-- redis分布式锁 -->
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson</artifactId>
+            <version>3.13.6</version>
+        </dependency>
+        <!-- 工具包 -->
+        <dependency>
+            <groupId>com.zanxiang.game</groupId>
+            <artifactId>game-common</artifactId>
+            <version>${game-common.vertion}</version>
+        </dependency>
+        <!-- 解决 redisTemplate序列化和反序列化 java.time包下面的类的问题 -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+        </dependency>
+        <!-- Apache Lang3 -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.8.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+            <version>2.3.2.RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
 
 </project>

+ 60 - 0
game-module/game-common-redis/src/main/java/com/zanxiang/common/redis/annotation/Lock.java

@@ -0,0 +1,60 @@
+package com.zanxiang.common.redis.annotation;
+
+import java.lang.annotation.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * <p>
+ * 分布式锁注解
+ * </p>
+ *
+ * @author zhengwangeng
+ * @since 2020/10/19
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface Lock {
+
+    /**
+     * 锁前缀
+     *
+     * @return
+     */
+    String prefix();
+
+    /**
+     * 锁的 key值(SpringEL表达式)
+     *
+     * @return
+     */
+    String key() default "";
+
+    /**
+     * 持锁时间,超时将丢失锁, 0标识永久持有锁
+     *
+     * @return
+     */
+    long keepLockTime() default 0L;
+
+    /**
+     * 获取锁的耗时, 超时返回失败,默认最0s,不等待
+     *
+     * @return
+     */
+    long tryLockWaitTime() default 0L;
+
+    /**
+     * 时间单位, 默认秒
+     *
+     * @return
+     */
+    TimeUnit timeUnit() default TimeUnit.SECONDS;
+
+    /**
+     * 执行完成是否释放锁
+     *
+     * @return
+     */
+    boolean autoUnLock() default true;
+}

+ 89 - 0
game-module/game-common-redis/src/main/java/com/zanxiang/common/redis/aspect/LockAspect.java

@@ -0,0 +1,89 @@
+package com.zanxiang.common.redis.aspect;
+
+import com.zanxiang.common.redis.annotation.Lock;
+import com.zanxiang.common.redis.exception.LockException;
+import com.zanxiang.common.redis.utils.DistributedLockUtil;
+import com.zanxiang.common.utils.StringUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.context.expression.MethodBasedEvaluationContext;
+import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+
+/**
+ * <p>
+ * 锁切面
+ * </p>
+ *
+ * @author zhengwangeng
+ * @since 2020/10/19
+ */
+@Slf4j
+@Aspect
+@Component
+public class LockAspect {
+    private static final ExpressionParser EL_PARSER = new SpelExpressionParser();
+
+    @Around("@annotation(com.zanxiang.common.redis.annotation.Lock)")
+    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
+        // 获取切入点方法对象
+        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
+        // 从方法上获取自定义注解 Lock, 获取key值, 锁配置等信息
+        Lock lockInfo = method.getAnnotation(Lock.class);
+        // 获得锁的key
+        String lockKey = this.getLockKey(joinPoint, method, lockInfo);
+        // 获得锁
+        boolean lockResult = DistributedLockUtil.tryLock(lockKey, lockInfo.keepLockTime(), lockInfo.tryLockWaitTime(), lockInfo.timeUnit());
+        if (!lockResult) {
+            throw new LockException("加锁失败!");
+        }
+        try {
+            return joinPoint.proceed();
+        } finally {
+            if (lockInfo.autoUnLock()) {
+                DistributedLockUtil.unlock(lockKey);
+            }
+        }
+    }
+
+
+    /**
+     * 获取锁的key值
+     *
+     * @param joinPoint 请求上下文
+     * @param method    方法
+     * @param lockInfo  锁设置信息
+     * @return
+     */
+    private String getLockKey(ProceedingJoinPoint joinPoint, Method method, Lock lockInfo) {
+        if (StringUtils.isBlank(lockInfo.prefix())) {
+            throw new LockException("加锁失败!锁前缀不能为空");
+        }
+        if (StringUtils.isBlank(lockInfo.key())) {
+            return lockInfo.prefix();
+        }
+        // 通过解析字节码文件读取方法的参数名
+        LocalVariableTableParameterNameDiscoverer methodParamsDiscover = new LocalVariableTableParameterNameDiscoverer();
+        // 实例化spring el上下文
+        StandardEvaluationContext context = new MethodBasedEvaluationContext(joinPoint.getTarget(), method, joinPoint.getArgs(), methodParamsDiscover);
+        // 获取切入点方法中所有的参数
+        String[] params = methodParamsDiscover.getParameterNames(method);
+        if (params != null && params.length > 0) {
+            // 将切入点方法参数值按顺序放入 el上下文
+            for (int i = 0; i < params.length; i++) {
+                context.setVariable(params[i], joinPoint.getArgs()[i]);
+            }
+        }
+        String lockKey = EL_PARSER.parseExpression(lockInfo.key()).getValue(context, String.class);
+        return lockInfo.prefix() + lockKey;
+    }
+
+}

+ 123 - 0
game-module/game-common-redis/src/main/java/com/zanxiang/common/redis/configure/RedisConfig.java

@@ -0,0 +1,123 @@
+package com.zanxiang.common.redis.configure;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+import com.zanxiang.common.constant.Constants;
+import org.springframework.cache.annotation.CachingConfigurerSupport;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+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.core.RedisTemplate;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+import java.time.Duration;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * redis配置
+ *
+ * @author ruoyi
+ */
+@Configuration
+@EnableCaching
+public class RedisConfig extends CachingConfigurerSupport {
+
+    @Bean
+    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
+        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+        redisTemplate.setConnectionFactory(connectionFactory);
+        // 值采用json序列化
+        redisTemplate.setValueSerializer(valueSerializer());
+        redisTemplate.setKeySerializer(keySerializer());
+        // 设置hash key 和value序列化模式
+        redisTemplate.setHashKeySerializer(keySerializer());
+        redisTemplate.setHashValueSerializer(valueSerializer());
+        redisTemplate.afterPropertiesSet();
+        return redisTemplate;
+    }
+
+    @Bean
+    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
+        return new StringRedisTemplate(factory);
+    }
+
+    @Bean
+    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
+        // 生成一个默认配置,通过config对象即可对缓存进行自定义配置
+        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
+                // 默认缓存一小时
+                .entryTtl(Duration.ofHours(2))
+                .computePrefixWith(cacheName -> Constants.REDIS_CACHE_PREFIX + cacheName)
+                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
+                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
+                //.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
+                // 不缓存空值
+                .disableCachingNullValues();
+
+        // 使用自定义的缓存配置初始化一个cacheManager
+        return RedisCacheManager.builder(connectionFactory)
+                .cacheDefaults(config)
+                // 对指定 key运用特殊配置
+                .transactionAware()
+                .build();
+    }
+
+    @Bean
+    public RedisSerializer<String> keySerializer() {
+        return new StringRedisSerializer();
+    }
+
+    @Bean
+    public Jackson2JsonRedisSerializer<Object> valueSerializer() {
+        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
+        Jackson2JsonRedisSerializer<Object> jsonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
+        ObjectMapper om = new ObjectMapper();
+        // json里面有多余字段序列化时不会失败
+        om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        // 指定要序列化的域,只序列化字段(包括 private字段),不对 get、set及 isXxx进行序列化。ANY指所有作用域的字段,包括 private
+        om.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
+                .setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE)
+                .setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE)
+                .setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE);
+
+        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
+        //om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
+                ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
+
+        // 解决 java.time包里面的类的序列化问题
+        om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+        JavaTimeModule timeModule = new JavaTimeModule();
+        timeModule.addDeserializer(LocalDate.class,
+                new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+        timeModule.addDeserializer(LocalDateTime.class,
+                new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+        timeModule.addSerializer(LocalDate.class,
+                new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+        timeModule.addSerializer(LocalDateTime.class,
+                new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+        om.registerModule(timeModule);
+        jsonSerializer.setObjectMapper(om);
+
+        return jsonSerializer;
+    }
+}

+ 46 - 0
game-module/game-common-redis/src/main/java/com/zanxiang/common/redis/configure/RedissonConfig.java

@@ -0,0 +1,46 @@
+package com.zanxiang.common.redis.configure;
+
+import com.zanxiang.common.redis.utils.DistributedLockUtil;
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * <p>
+ * Redisson配置
+ * </p>
+ *
+ * @author zhengwangeng
+ * @since 2020/10/19
+ */
+@Configuration
+@EnableConfigurationProperties(RedisProperties.class)
+public class RedissonConfig {
+
+    @Bean
+    public RedissonClient init(RedisProperties redisProperties) {
+        Config config = new Config();
+        // 使用单节点模式
+        config.useSingleServer()
+                // 节点地址 -》可以通过host:port的格式来指定节点地址。
+                .setAddress("redis://" + redisProperties.getHost() + ":" + redisProperties.getPort())
+//                .setAddress("redis://127.0.0.1:6379")
+                // 连接池 默认 64
+//                .setConnectionPoolSize()
+                // 用于发布和订阅连接的连接池最大容量。连接池的连接数量自动弹性伸缩。 默认60
+//                .setSubscriptionConnectionPoolSize(0)
+                // 最小空闲连接数, 最小保持连接数(长连接)。长期保持一定数量的连接有利于提高瞬时写入反应速度。 默认32
+//                .setConnectionMinimumIdleSize(0)
+                // 默认1库
+                .setDatabase(1)
+                .setPassword(redisProperties.getPassword());
+        RedissonClient redissonClient = Redisson.create(config);
+        DistributedLockUtil.setRedissonClient(redissonClient);
+        return redissonClient;
+    }
+
+}

+ 25 - 0
game-module/game-common-redis/src/main/java/com/zanxiang/common/redis/exception/LockException.java

@@ -0,0 +1,25 @@
+package com.zanxiang.common.redis.exception;
+
+/**
+ * <p>
+ * 锁异常
+ * </p>
+ *
+ * @author zhengwangeng
+ * @since 2020/10/19
+ */
+public class LockException extends RuntimeException {
+    private static final long serialVersionUID = 8247610319171014183L;
+
+    public LockException(Throwable e) {
+        super(e.getMessage(), e);
+    }
+
+    public LockException(String message) {
+        super(message);
+    }
+
+    public LockException(String message, Throwable throwable) {
+        super(message, throwable);
+    }
+}

+ 232 - 0
game-module/game-common-redis/src/main/java/com/zanxiang/common/redis/utils/DistributedLockUtil.java

@@ -0,0 +1,232 @@
+package com.zanxiang.common.redis.utils;
+
+import com.zanxiang.common.utils.KeyBuilder;
+import com.zanxiang.common.utils.StringUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.redisson.RedissonMultiLock;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+import org.springframework.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * <p>
+ * 分布式锁帮助类
+ * </p>
+ *
+ * @author zhengwangeng
+ * @since 2020/10/19
+ */
+@Slf4j
+public final class DistributedLockUtil {
+
+    private static RedissonClient redissonClient;
+
+    private static AtomicBoolean initFlag = new AtomicBoolean(false);
+
+    private static final String KEY_PREFIX = KeyBuilder.build("ZX-GAME", "DISTRIBUTE", "LOCK");
+
+    private DistributedLockUtil() {
+
+    }
+
+    public static void setRedissonClient(RedissonClient client) {
+        if (initFlag.compareAndSet(false, true)) {
+            redissonClient = client;
+        }
+    }
+
+    /**
+     * 非过期锁
+     *
+     * @param key key
+     * @return
+     */
+    public static void lock(String key) {
+        RLock rLock = getLock(key);
+        rLock.lock();
+    }
+
+    /**
+     * 过期锁
+     *
+     * @param key          key
+     * @param keepLockTime 持锁时间
+     * @param timeUnit     时间单位
+     * @return
+     */
+    public static void lock(String key, Long keepLockTime, TimeUnit timeUnit) {
+        if (null == timeUnit) {
+            throw new NullPointerException("timeUnit cant be null!");
+        }
+        RLock rLock = getLock(key);
+        rLock.lock(keepLockTime, timeUnit);
+    }
+
+    /**
+     * 尝试获取一次非过期锁
+     *
+     * @param key key
+     * @return
+     */
+    public static Boolean tryLock(String key) {
+        RLock rLock = getLock(key);
+        return rLock.tryLock();
+    }
+
+
+    /**
+     * 尝试在一段时间内,持续的去获取非过期锁
+     *
+     * @param key             key
+     * @param tryLockWaitTime 尝试等待时间
+     * @param timeUnit        时间单位
+     * @return
+     */
+    public static Boolean tryLock(String key, Long tryLockWaitTime, TimeUnit timeUnit) {
+        if (null == timeUnit) {
+            throw new NullPointerException("timeUnit cant be null!");
+        }
+        RLock rLock = getLock(key);
+        try {
+            if (null != tryLockWaitTime && tryLockWaitTime > 0) {
+                return rLock.tryLock(tryLockWaitTime, timeUnit);
+            } else {
+                return rLock.tryLock();
+            }
+        } catch (InterruptedException e) {
+            log.error("在为key:{}尝试获取锁的时候,未知异常!", key, e);
+            log.error(e.getMessage(), e);
+        }
+        return false;
+    }
+
+    /**
+     * 尝试在一段时间内,持续的去获取过期锁
+     *
+     * @param key             key
+     * @param leaseTime       锁过期时间
+     * @param tryLockWaitTime 尝试等待时间
+     * @param timeUnit        时间单位
+     * @return
+     */
+    public static Boolean tryLock(String key, Long leaseTime, Long tryLockWaitTime, TimeUnit timeUnit) {
+        if (null == timeUnit) {
+            throw new NullPointerException("timeUnit cant be null!");
+        }
+        RLock rLock = getLock(key);
+        try {
+            if (null != tryLockWaitTime && tryLockWaitTime > 0) {
+                if (null != leaseTime && leaseTime > 0) {
+                    return rLock.tryLock(tryLockWaitTime, leaseTime, timeUnit);
+                } else {
+                    return rLock.tryLock(tryLockWaitTime, timeUnit);
+                }
+            } else {
+                if (null != leaseTime && leaseTime > 0) {
+                    return rLock.tryLock(0, leaseTime, timeUnit);
+                } else {
+                    return rLock.tryLock();
+                }
+            }
+        } catch (InterruptedException e) {
+            log.error("在为key:{}尝试获取锁的时候,未知异常!", key, e);
+            log.error(e.getMessage(), e);
+        }
+        return false;
+    }
+
+    /**
+     * 释放锁
+     *
+     * @param key
+     */
+    public static void unlock(String key) {
+        RLock rLock = getLock(key);
+        if (rLock.isLocked()) {// 先判断要解锁的 key是否已被锁定(防止锁到期自动消除)
+            if (rLock.isHeldByCurrentThread()) {// 是否被当前线程保持(防止锁到期自动消除后被其它实例或线程加锁)
+                rLock.unlock();
+            }
+        }
+    }
+
+
+    /**
+     * 获取RLock对象
+     *
+     * @param key
+     * @return
+     */
+    private static RLock getLock(String key) {
+        if (StringUtils.isBlank(key)) {
+            throw new NullPointerException("key can't be null!");
+        }
+        return redissonClient.getLock(KEY_PREFIX + key);
+//        return redissonClient.getLock(KeyBuilder.build(KEY_PREFIX, key));
+    }
+
+    /**
+     * 联锁, 当keys中的每个key都上锁,才说明被锁
+     *
+     * @param keys      锁key值集合
+     * @param leaseTime 锁过期时间
+     * @param waitTime  尝试等待时间
+     * @param timeUnit  时间单位
+     * @return
+     */
+    public static Boolean multiLock(Collection<String> keys, Long waitTime, Long leaseTime, TimeUnit timeUnit) {
+        if (CollectionUtils.isEmpty(keys)) {
+            throw new NullPointerException("keys cant be null!");
+        }
+        if (null == timeUnit) {
+            throw new NullPointerException("timeUnit cant be null!");
+        }
+        List<RLock> rLocks = new ArrayList<>(keys.size());
+        for (String key : keys) {
+            rLocks.add(getLock(key));
+        }
+        RedissonMultiLock redissonMultiLock = new RedissonMultiLock(rLocks.toArray(new RLock[keys.size()]));
+        try {
+            if (null != waitTime && waitTime > 0) {
+                if (null != leaseTime && leaseTime > 0) {
+                    return redissonMultiLock.tryLock(waitTime, leaseTime, timeUnit);
+                } else {
+                    return redissonMultiLock.tryLock(waitTime, timeUnit);
+                }
+            } else {
+                if (null != leaseTime && leaseTime > 0) {
+                    return redissonMultiLock.tryLock(0, leaseTime, timeUnit);
+                } else {
+                    return redissonMultiLock.tryLock();
+                }
+            }
+        } catch (InterruptedException e) {
+            log.error("在为keys:{}尝试获取联锁的时候,未知异常!", keys, e);
+            log.error(e.getMessage(), e);
+        }
+        return false;
+    }
+
+    /**
+     * 解联锁
+     *
+     * @param keys 锁key值集合
+     * @return
+     */
+    public static void multiUnLock(Collection<String> keys) {
+        if (CollectionUtils.isEmpty(keys)) {
+            throw new NullPointerException("keys cant be null!");
+        }
+        List<RLock> rLocks = new ArrayList<>(keys.size());
+        for (String key : keys) {
+            rLocks.add(getLock(key));
+        }
+        RedissonMultiLock redissonMultiLock = new RedissonMultiLock(rLocks.toArray(new RLock[keys.size()]));
+        redissonMultiLock.unlock();
+    }
+}

+ 6 - 0
game-module/game-common-redis/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,6 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  com.zanxiang.common.redis.configure.RedisConfig,\
+  com.zanxiang.common.redis.configure.RedissonConfig,\
+  com.zanxiang.common.redis.aspect.LockAspect
+
+