SpringBoot集成Redis | 使用注解动态切换Redis库

举报
慌途L 发表于 2019/04/03 19:36:19 2019/04/03
【摘要】 SpringBoot集成Redis | 使用注解动态切换Redis库由于工作中不同的数据信息缓存在不同的redis的16个库中,当某个地方需要用到的时候,切换redis库不方便,下面讲解的即可简便操作,可直接集成于common包中本人已将项目打包到github,有需要的可以直接去拿项目地址-github:https://github.com/Linsider/redis大家看完可以导入项目跑...


SpringBoot集成Redis | 使用注解动态切换Redis库


由于工作中不同的数据信息缓存在不同的redis的16个库中,当某个地方需要用到的时候,切换redis库不方便,下面讲解的即可简便操作,可直接集成于common包中


本人已将项目打包到github,有需要的可以直接去拿

项目地址-github:https://github.com/Linsider/redis

大家看完可以导入项目跑一跑,有疑问可以在评论区留言,看到即回复



一、目录结构

image.png

二、创建redis切换库所需注解、类

2.1 application.yml配置

# Tomcat
server:
  tomcat:
    uri-encoding: UTF-8
    max-threads: 1000
    min-spare-threads: 30
  port: 8088
  servlet:
    context-path: /redis-demo

# mysql
spring:
  http:
    multipart:
      max-file-size: 100MB
      max-request-size: 100MB
      enabled: true
  redis:
    database: 0
    host: localhost
    port: 6379
    password:    # 密码(默认为空)
    timeout: 6000ms  # 连接超时时长(毫秒)
    jedis:
      pool:
        max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
        max-wait: -1ms      # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-idle: 10      # 连接池中的最大空闲连接
        min-idle: 5       # 连接池中的最小空闲连接
  cache:
    type: none

huangtuL:
  redis:
    open: false  # 是否开启redis缓存  true开启   false关闭

logging:
  level: debug
  level.com.example: debug
  path: logs/
  file: huangtuL.log




2.2 创建自定义注解 RedisSelect


import java.lang.annotation.*;

/**
 * author huangtuL
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisSelect {
    /**
     * redis库
     * @return
     */
    int value() default 0;
}



2.3 Redis配置类 RedisConfig (下面的注解即可直接回去yml中的redis配置)

import com.example.redis.SelectableRedisTemplate;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
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;
import org.springframework.context.annotation.Primary;
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 redis.clients.jedis.JedisPoolConfig;

import java.time.Duration;

/**
 * Redis配置相关
 * author huangtuL
 */
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)// 自动获取application.yml中的配置
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {
    private RedisProperties properties;

    public RedisConfig(RedisProperties properties){
        this.properties = properties;
    }

    @Bean
    @Primary
    public JedisConnectionFactory jedisConnectionFactory(){
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName(properties.getHost());
        config.setPort(properties.getPort());
        config.setPassword(RedisPassword.of(properties.getPassword()));
        config.setDatabase(properties.getDatabase());
        return new JedisConnectionFactory(config, getJedisClientConfiguration());
    }

    private JedisClientConfiguration getJedisClientConfiguration() {
        JedisClientConfiguration.JedisClientConfigurationBuilder builder = JedisClientConfiguration.builder();
        if (properties.isSsl()) {
            builder.useSsl();
        }
        if (properties.getTimeout() != null) {
            Duration timeout = properties.getTimeout();
            builder.readTimeout(timeout).connectTimeout(timeout);
        }
        RedisProperties.Pool pool = properties.getJedis().getPool();
        if (pool != null) {
            builder.usePooling().poolConfig(jedisPoolConfig(pool));
        }
        return builder.build();
    }

    private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool pool) {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(pool.getMaxActive());
        config.setMaxIdle(pool.getMaxIdle());
        config.setMinIdle(pool.getMinIdle());
        if (pool.getMaxWait() != null) {
            config.setMaxWaitMillis(pool.getMaxWait().toMillis());
        }
        return config;
    }

    @Bean(name = "redisTemplate")
    @Primary
    public SelectableRedisTemplate redisTemplate() {
        SelectableRedisTemplate redisTemplate = new SelectableRedisTemplate();
        redisTemplate.setConnectionFactory(jedisConnectionFactory());
        return redisTemplate;
    }

}




2.4 redis工具类 RedisUtils(这没啥说的)

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类
 *
 * author huangtuL
 */
@Component
public class RedisUtils {
    @Autowired
    private StringRedisTemplate redisTemplate;
    private ValueOperations<String, String> valueOperations;

    @PostConstruct
    public void init(){
        this.valueOperations = redisTemplate.opsForValue();
    }

    /**  默认过期时长,单位:秒 */
    public final static long DEFAULT_EXPIRE = 60 * 60 * 24;
    /**  不设置过期时长 */
    public final static long NOT_EXPIRE = -1;

    public void set(String key, Object value, long expire){
        valueOperations.set(key, toJson(value));
        if(expire != NOT_EXPIRE){
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
    }

    public void set(String key, Object value){
        set(key, value, DEFAULT_EXPIRE);
    }

    public <T> T get(String key, Class<T> clazz, long expire) {
        String value = valueOperations.get(key);
        if(expire != NOT_EXPIRE){
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
        return value == null ? null : fromJson(value, clazz);
    }

    public <T> T get(String key, Class<T> clazz) {
        return get(key, clazz, NOT_EXPIRE);
    }

    public String get(String key, long expire) {
        String value = valueOperations.get(key);
        if(expire != NOT_EXPIRE){
            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
        }
        return value;
    }

    public String get(String key) {
        return get(key, NOT_EXPIRE);
    }

    public void delete(String key) {
        redisTemplate.delete(key);
    }

    /**
     * Object转成JSON数据
     */
    private String toJson(Object object){
        if(object instanceof Integer || object instanceof Long || object instanceof Float ||
                object instanceof Double || object instanceof Boolean || object instanceof String){
            return String.valueOf(object);
        }
        return JSON.toJSONString(object);
    }

    /**
     * JSON数据,转成Object
     */
    private <T> T fromJson(String json, Class<T> clazz){
        return JSON.parseObject(json, clazz);
    }

}





2.5 redis切换库支持类 RedisSelectSupport、SelectableRedisTemplate (切换连接在这)

/**
 * author huangtuL
 */
public class RedisSelectSupport {
    private static final ThreadLocal<Integer> SELECT_CONTEXT = new ThreadLocal<>();

    public static void select(int db){
        SELECT_CONTEXT.set(db);
    }

    public static Integer getSelect(){
        return SELECT_CONTEXT.get();
    }
}


import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.StringRedisTemplate;

/**
 * author huangtuL
 */
public class SelectableRedisTemplate extends StringRedisTemplate {

    @Override
    protected RedisConnection createRedisConnectionProxy(RedisConnection pm) {
        return super.createRedisConnectionProxy(pm);
    }

    @Override
    protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
        Integer db;
        if((db = RedisSelectSupport.getSelect()) != null){
            connection.select(db);
        }
        return super.preProcessConnection(connection, existingConnection);
    }


}




2.6 切面类 RedisAspect (主要在这)

import com.example.annotation.RedisSelect;
import com.example.exception.RRException;
import com.example.redis.RedisSelectSupport;
import com.example.redis.SelectableRedisTemplate;
import com.example.util.RedisUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * Redis切面处理类
 *
 * author huangtuL
 */
@Aspect
@Component
public class RedisAspect {
    private Logger logger = LoggerFactory.getLogger(getClass());
    //是否开启redis缓存  true开启   false关闭
    @Value("${huangtuL.redis.open: false}")
    private boolean open;

    @Autowired
    private RedisUtils redisUtils;

    @Value("${spring.redis.database:0}")
    private int defaultDataBase;

    @Around("execution(* com.example.util.RedisUtils.*(..))")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Object result = null;
        if(open){
            try{
                result = point.proceed();
            }catch (Exception e){
                logger.error("redis error", e);
                throw new RRException("Redis服务异常");
            }
        }
        return result;
    }

    @Around("@annotation(com.example.annotation.RedisSelect)")
    @ConditionalOnBean(SelectableRedisTemplate.class)
    public Object configRedis(ProceedingJoinPoint point) throws Throwable{
        int db = defaultDataBase;
        try {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();

            RedisSelect config = method.getAnnotation(RedisSelect.class);
            if(config != null){
                db = config.value();
            }
            RedisSelectSupport.select(db);
            return point.proceed();
        } finally {
            RedisSelectSupport.select(defaultDataBase);
            logger.debug("redis reset {} to {}", db, defaultDataBase);
        }
    }
}



三、测试

3.1 测试类 TestRedisSelect


import com.example.annotation.RedisSelect;
import com.example.redis.RedisSelectSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 测试redis切换库
 * author huangtuL
 */
@RestController
@RequestMapping("redis")
public class TestRedisSelect {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @RequestMapping("/one")
    @RedisSelect(1)         //选择db1库
    public String selectOne(){
        String one = redisTemplate.opsForValue().get("one");
        return one;
    }

    @RequestMapping("/two")
    @RedisSelect(2)         //选择db2库
    public String selectTwo(){
        String two = redisTemplate.opsForValue().get("two");
        return two;
    }

    /**
     * 同一个方法中切换不同的redis库
     * @return
     */
    @RequestMapping("/three")
    @RedisSelect(2)         //选择db2库
    public String selectThree(){
        String two = redisTemplate.opsForValue().get("two");
        System.out.println(two);
        RedisSelectSupport.select(3);//此处切换到db3库
        String three = redisTemplate.opsForValue().get("three");
        System.out.println(three);
        return three;
    }
}


3.2 redis中写入测试数据

image.png



3.3 测试结果:(打印的 redis reset 表示从哪个库切换回默认的库)

第一个方法,从0切到1库:127.0.0.1:8088/redis-demo/redis/one

image.png


第二个方法,从0切到2库:127.0.0.1:8088/redis-demo/redis/two

image.png


第三个方法,从0切到2库,同一个方法中再切到3库:127.0.0.1:8088/redis-demo/redis/three

image.png


至此,演示完毕。大家看完可以导入项目跑一跑,有疑问可以在评论区留言,看到即回复



【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。