All Downloads are FREE. Search and download functionalities are using the official Maven repository.

matrix.boot.redis.config.RedisAutoConfiguration Maven / Gradle / Ivy

The newest version!
package matrix.boot.redis.config;

import matrix.boot.common.exception.ServiceException;
import matrix.boot.common.utils.AssertUtil;
import matrix.boot.common.utils.StringUtil;
import matrix.boot.redis.properties.RedisProperties;
import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter;
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.*;
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.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

import java.time.Duration;
import java.util.*;

/**
 * redis自动装配类
 *
 * @author WangCheng
 */
@EnableConfigurationProperties(RedisProperties.class)
@ConditionalOnProperty(value = {"matrix.redis.enabled"})
@EnableCaching
public class RedisAutoConfiguration {

    /**
     * 默认参数 @Cacheable注解用
     */
    public static final String DEFAULT_VALUE = "DEFAULT";

    /**
     * 默认key生成方式 @Cacheable注解用
     */
    public static final String DEFAULT_GENERATOR = "wiselyKeyGenerator";

    /**
     * redis配置
     */
    private final RedisProperties redisProperties;

    public RedisAutoConfiguration(RedisProperties redisProperties) {
        this.redisProperties = redisProperties;
    }

    /**
     * 创建redis模板
     *
     * @param factory        redis连接工厂
     * @param keyClassType   键类型
     * @param valueClassType 值类型
     * @param             键泛型
     * @param             值泛型
     * @return redis模板
     */
    private static  RedisTemplate createRedisTemplate(RedisConnectionFactory factory, Class keyClassType, Class valueClassType) {
        RedisTemplate redisTemplate = new RedisTemplate<>();
        //定义json序列化
        RedisSerializer redisSerializer = new GenericJackson2JsonRedisSerializer();
        if (String.class.equals(keyClassType)) {
            //如果键为String类型,设置键序列化为string序列化
            RedisSerializer stringRedisSerializer = new StringRedisSerializer();
            redisTemplate.setKeySerializer(stringRedisSerializer);
            redisTemplate.setHashKeySerializer(stringRedisSerializer);
        } else {
            //设置为json序列化
            redisTemplate.setKeySerializer(redisSerializer);
            redisTemplate.setHashKeySerializer(redisSerializer);
        }
        //设置值为json序列化
        redisTemplate.setValueSerializer(redisSerializer);
        redisTemplate.setHashValueSerializer(redisSerializer);
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }

    /**
     * 获取redis连接工厂
     *
     * @return redis工厂
     */
    @Bean
    @Order(value = 1)
    public JedisConnectionFactory redisConnectionFactory() {
        JedisClientConfiguration.JedisClientConfigurationBuilder builder = JedisClientConfiguration.builder();
        //设置连接超时时间
        Duration duration = Duration.ofSeconds(redisProperties.getTimeout());
        builder.readTimeout(duration).connectTimeout(duration);
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        //设置最大空闲数
        poolConfig.setMaxIdle(20);
        //设置最大连接数
        poolConfig.setMaxTotal(redisProperties.getMaxTotal());
        //设置最大等待时间10s
        poolConfig.setMaxWaitMillis(10000L);
        builder.usePooling().poolConfig(poolConfig);
        return this.getRedisConnectionFactory(builder.build());
    }

    /**
     * 获取redis工厂
     *
     * @param client redis客户端
     * @return redis工厂
     */
    private JedisConnectionFactory getRedisConnectionFactory(JedisClientConfiguration client) {
        if (redisProperties.getStandalone().isEnabled()) {
            //单机模式
            RedisProperties.Standalone standaloneConfig = redisProperties.getStandalone();
            String hostName = standaloneConfig.getHost();
            AssertUtil.notNullTip(hostName, "matrix.redis.host");
            Integer database = standaloneConfig.getDatabase();
            AssertUtil.notNullTip(database, "matrix.redis.database");
            String password = standaloneConfig.getPassword();
            String port = standaloneConfig.getPort();
            AssertUtil.notNullTip(port, "matrix.redis.port");
            RedisStandaloneConfiguration standalone = new RedisStandaloneConfiguration();
            standalone.setHostName(hostName);
            standalone.setDatabase(database);
            if (!StringUtil.isEmpty(password)) {
                standalone.setPassword(RedisPassword.of(password));
            }
            standalone.setPort(Integer.parseInt(port));
            return new JedisConnectionFactory(standalone, client);
        }
        if (redisProperties.getSentinel().isEnabled()) {
            //哨兵模式
            RedisProperties.Sentinel sentinelConfig = redisProperties.getSentinel();
            String master = sentinelConfig.getMaster();
            AssertUtil.notNullTip(master, "matrix.redis.sentinel.master");
            String nodes = sentinelConfig.getNodes();
            AssertUtil.notNullTip(nodes, "matrix.redis.sentinel.nodes");
            String password = sentinelConfig.getPassword();
            Integer database = sentinelConfig.getDatabase();
            AssertUtil.notNullTip(database, "matrix.redis.sentinel.database");
            RedisSentinelConfiguration sentinel = new RedisSentinelConfiguration();
            sentinel.master(master);
            sentinel.setSentinels(this.parseRedisNodes(nodes));
            if (!StringUtil.isEmpty(password)) {
                sentinel.setPassword(RedisPassword.of(password));
            }
            sentinel.setDatabase(database);
            return new JedisConnectionFactory(sentinel, client);
        }
        if (redisProperties.getCluster().isEnabled()) {
            //集群模式
            RedisProperties.Cluster clusterConfig = redisProperties.getCluster();
            String nodes = clusterConfig.getNodes();
            AssertUtil.notNullTip(nodes, "matrix.redis.cluster.nodes");
            String password = clusterConfig.getPassword();
            Integer maxRedirects = clusterConfig.getMaxRedirects();
            AssertUtil.notNullTip(maxRedirects, "matrix.redis.cluster.max-redirects");
            RedisClusterConfiguration cluster = new RedisClusterConfiguration();
            cluster.setClusterNodes(this.parseRedisNodes(nodes));
            if (!StringUtil.isEmpty(password)) {
                cluster.setPassword(RedisPassword.of(password));
            }
            cluster.setMaxRedirects(maxRedirects);
            return new JedisConnectionFactory(cluster, client);
        }
        throw new ServiceException("standalone, sentinel, cluster don't deactivate all");
    }

    /**
     * 根据配置解析redis节点
     *
     * @param data 用户配置数据
     * @return redis节点
     */
    private List parseRedisNodes(String data) {
        AssertUtil.matchRegex("([^,:]+:\\d{1,5},)*([^,:]+:\\d{1,5}){1}",
                data, "格式为: host:port,host:port", null);
        List nodes = new ArrayList<>();
        for (String text : data.split(",")) {
            if (!StringUtil.isEmpty(text)) {
                String[] parts = text.split(":");
                AssertUtil.state(parts.length == 2, "Must be defined as 'host:port'");
                nodes.add(new RedisNode(parts[0], Integer.parseInt(parts[1])));
            }
        }
        return nodes;
    }

    /**
     * spring @Cacheable注解缓存管理器
     *
     * @param redisConnectionFactory redis连接工厂
     * @return 缓存管理器
     */
    @Bean
    @Order(value = 2)
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        Long expire = redisProperties.getDefaultExpire();
        if (expire != null) {
            config = config.entryTtl(Duration.ofSeconds(expire));
        }
        return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();
    }

    /**
     * spring @Cacheable key生成规则
     *
     * @return key规则生成器
     */
    @Bean
    @Order(value = 3)
    public KeyGenerator wiselyKeyGenerator() {
        return (target, method, params) -> {
            List cacheKey = new ArrayList<>();
            cacheKey.add(target.getClass().getName());
            cacheKey.add(method.getName());
            if (params.length > 0) {
                StringBuilder sb = new StringBuilder();
                for (Object obj : params) {
                    sb.append(obj != null ? String.valueOf(obj) : "NONE");
                }
                cacheKey.add(sb.toString());
            }
            return String.join(":", cacheKey);
        };
    }

    /**
     * 默认的stringRedisTemplate
     *
     * @param factory redis工厂
     * @return 默认的模板
     */
    @Bean(name = "redisTemplate")
    @Order(value = 4)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate redisTemplate = new StringRedisTemplate();
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }

    /**
     * 默认的objectRedisTemplate
     *
     * @param factory redis工厂
     * @return 默认的object模板
     */
    @Bean
    @Order(value = 5)
    public RedisTemplate objectRedisTemplate(RedisConnectionFactory factory) {
        return createRedisTemplate(factory, Object.class, Object.class);
    }

    /**
     * 默认的stringRedisTemplate
     *
     * @param factory redis工厂
     * @return 默认的string模板
     */
    @Bean
    @Order(value = 6)
    public RedisTemplate defaultRedisTemplate(RedisConnectionFactory factory) {
        return createRedisTemplate(factory, String.class, Object.class);
    }

    /**
     * 排除自动装配过滤器
     */
    public static class ExcludeAutoConfigurationFilter implements AutoConfigurationImportFilter {

        private static final Set SHOULD_SKIP = new HashSet<>(Collections.singletonList(
                "org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration"));

        @Override
        public boolean[] match(String[] classNames, AutoConfigurationMetadata metadata) {
            boolean[] matches = new boolean[classNames.length];
            for (int i = 0; i < classNames.length; i++) {
                matches[i] = !SHOULD_SKIP.contains(classNames[i]);
            }
            return matches;
        }
    }
}