项目集成 Redis ( Jedis )

加入依赖:

<!-- Redisson依赖,根据需求,可选的 -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.12.0</version>
</dependency>
<!--Jedis 客户端-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.10.2</version>
</dependency>

配置属性文件 :application-redis.yml

spring:
  redis:
    #----- 自定义分组的连接    [custom]
    # 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
    BlockWhenExhausted: true
    # 设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)
    evictionPolicyClassName: org.apache.commons.pool2.impl.DefaultEvictionPolicy
    # 是否启用pool的jmx管理功能, 默认true
    jmxEnabled: true
    # 是否启用后进先出, 默认true
    lifo: true
    # 最大空闲连接数, 默认8个
    maxIdle: 8
    # 最小空闲连接数, 默认0
    minIdle: 0
    # 最大连接数, 默认8个
    maxTotal: 8
    # 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1
    maxWaitMillis: -1
    # 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
    minEvictableIdleTimeMillis: 1800000
    # 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
    numTestsPerEvictionRun: 3
    # 对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断  (默认逐出策略)
    SoftMinEvictableIdleTimeMillis: 1800000
    # 在获取连接的时候检查有效性, 默认false
    testOnBorrow: false
    # 在空闲时检查有效性, 默认false
    testWhileIdle: false
    # 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
    timeBetweenEvictionRunsMillis: -1
 
    clients:
      - host: 192.168.2.218
        # 端口,默认6379
        port: 6379
        # 超时,默认2000
        timeout: 2000
        # 连接超时,默认timeout
        connectionTimeout: 2000
        # 读取超时,默认timeout
        soTimeout: 2000
        # 密码
        password: jmrh
        # 数据库序号,默认0
        database: 0
        # 客户端名称
        clientName: carleasoft
        # SSL连接,默认false
        ssl: false

配置属性对应 Java 类:

@Data
@ConfigurationProperties(prefix = "spring.redis")
public class IRedisProperties implements Serializable {
    private static final long serialVersionUID = 1L;
 
    private List<Client> clients = new ArrayList<>();
 
    // 自定义分组的连接[custom]
    private Boolean blockWhenExhausted;
    // 设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)
    private String evictionPolicyClassName;
    // 是否启用pool的jmx管理功能, 默认true
    private Boolean jmxEnabled;
    // 是否启用后进先出, 默认true
    private Boolean lifo;
    // 最大空闲连接数, 默认8个
    private int maxIdle;
    // 最小空闲连接数, 默认0
    private int minIdle;
    // 最大连接数, 默认8个
    private int maxTotal;
    // 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1
    private int maxWaitMillis;
    // 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
    private Long minEvictableIdleTimeMillis;
    // 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
    private Integer numTestsPerEvictionRun;
    // 对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断  (默认逐出策略)
    private Long softMinEvictableIdleTimeMillis;
    // 创建链接时检测有效性
    private boolean testOnCreate = false;
    // 在获取连接的时候检查有效性, 默认false
    private Boolean testOnBorrow;
    // 返回时检测有效性
    private boolean testOnReturn = false;
    // 在空闲时检查有效性, 默认false
    private Boolean testWhileIdle;
    // 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
    private int timeBetweenEvictionRunsMillis;
 
 
    @Data
    public static class Client {
        private String host = "localhost";
        // 端口,默认6379
        private int port = 6379;
        // 超时,默认2000
        private int timeout = 2000;
        // 连接超时,默认timeout
        private int connectionTimeout = 2000;
        // 读取超时,默认timeout
        private int soTimeout = 2000;
        // 密码
        private String password;
        // 数据库序号,默认0
        private int database = 0;
        // 客户端名称
        private String clientName;
        // SSL连接,默认false
        private Boolean ssl = false;
    }
}

配置:

IRedis 是Redis 的具体操作类,

IRedisAspect 是 Redis 的切面类,用来代理关闭 Jedis 的,

RedissonClient 是配置 Redisson 分布式锁的,自行按需求选择取舍

这里可以自行选需要的类进行配置

@Slf4j
@Configuration
@ComponentScan(basePackages = {"com.xxx.xxx.redis.service"})
@EnableConfigurationProperties(value = {IRedisProperties.class})
public class IRedisConfig {
 
    @Autowired
    private IRedisProperties redisProperties;
    private JedisPool jedisPool;
 
    @Bean
    @ConditionalOnProperty(name = "spring.redis")
    public JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig config = BeanUtil.copyProperties(redisProperties, JedisPoolConfig.class);
        return config;
    }
 
 
    @Bean
    @ConditionalOnBean(JedisPoolConfig.class)
    public JedisPool createPool(JedisPoolConfig config) {
        RedisProperties.Client client = redisProperties.getClients().get(0);
        jedisPool = new JedisPool(config,
                // 地址
                client.getHost(),
                // 端口
                client.getPort(),
                // 连接超时
                client.getConnectionTimeout(),
                // 读取数据超时
                client.getSoTimeout(),
                // 密码
                client.getPassword(),
                // 默认数据库序号
                client.getDatabase(),
                // 客户端名
                client.getClientName(),
                // 是否使用SSL
                client.getSsl(),
                // SSL相关,使用默认
                null, null, null);
        log.info("[ IRedis ] |- Bean [JedisPool] Auto Configure.");
        return jedisPool;
    }
 
 
    @Bean
    @ConditionalOnBean(JedisPoolConfig.class)
    public ShardedJedisPool shardedJedisPool(JedisPoolConfig config) {
        List<JedisShardInfo> shards = redisProperties.getClients().stream()
                .map(this::createShardInfo)
                .collect(Collectors.toList());
        ShardedJedisPool shardedJedisPool = new ShardedJedisPool(config, shards);
        return shardedJedisPool;
    }
 
 
    private JedisShardInfo createShardInfo(RedisProperties.Client client){
        JedisShardInfo jedisShardInfo = new JedisShardInfo(
                // 地址
                client.getHost(),
                // 端口
                client.getPort(),
                // 客户端名称
                client.getClientName(),
                // 是否使用SSL
                client.getSsl(),
                // SSL相关,使用默认
                null, null, null);
        jedisShardInfo.setPassword(client.getPassword());
        jedisShardInfo.setConnectionTimeout(client.getConnectionTimeout());
        jedisShardInfo.setSoTimeout(client.getSoTimeout());
        return jedisShardInfo;
    }
 
 
    public void close() {
        jedisPool.close();
    }
 
    /**
     * Redisson客户端
     *
     * @return
     */
    @Bean
    @ConditionalOnProperty(name = "spring.redis.clients")
    public RedissonClient redissonClient() {
        RedisProperties.Client client = redisProperties.getClients().get(0);
        Config config = new Config();
        config.useSingleServer()
                .setAddress("redis://" + client.getHost() + ":" + client.getPort())
                .setPassword(client.getPassword())
                .setDatabase(0)
                .setConnectionPoolSize(redisProperties.getMaxTotal())
                .setConnectionMinimumIdleSize(2);
        RedissonClient redissonClient = Redisson.create(config);
        log.info("[CarleaSoft] |- Bean [RedissonClient] Auto Configure.");
        return redissonClient;
    }
}

Redis 具体操作工具类:

@Slf4j
@Component
public class IRedis {

    private static volatile ThreadLocal<Jedis> currentJedis = new ThreadLocal<>();
    private static volatile ThreadLocal<ShardedJedis> currentShardJedis = new ThreadLocal<>();

    @Autowired
    private JedisPool jedisPool;
    @Autowired
    private ShardedJedisPool shardedJedisPool;

    public Jedis getJedis() {
        Jedis jedis = jedisPool.getResource();
        jedis.select(0);
        return jedis;
    }


    public ShardedJedis shardedJedis() {
        ShardedJedis shardedJedis = shardedJedisPool.getResource();
        return shardedJedis;
    }


    public void close(Jedis jedis) {
        if (null != jedis) {
            jedis.close();
        }
    }

    public void close(ShardedJedis shardedJedis) {
        if (null != shardedJedis) {
            shardedJedis.close();
        }
    }

    /**
     * 此函数慎用
     *
     * @return
     */
    public Jedis threadJedis() {
        Jedis jedis = currentJedis.get();
        if (null == jedis) {
            jedis = this.getJedis();
            currentJedis.set(jedis);
        }
        return jedis;
    }

    public void closeJedis() {
        try {
            this.close(currentJedis.get());
        } finally {
            // 只会删除自己线程中的数据
            currentJedis.remove();
        }
    }


    /**
     * 此函数慎用
     *
     * @return
     */
    public ShardedJedis threadShardJedis() {
        ShardedJedis shardedJedis = currentShardJedis.get();
        if (null == shardedJedis) {
            shardedJedis = this.shardedJedis();
            currentShardJedis.set(shardedJedis);
        }
        return shardedJedis;
    }

    public void closeShardJedis() {
        try {
            this.close(currentShardJedis.get());
        } finally {
            // 只会删除自己线程中的数据
            currentShardJedis.remove();
        }
    }

}

 


Redis 切面类,作用是辅助关闭 Jedis的,可自行选择,若不需要则无需以下部分

@Aspect
@Component
@AllArgsConstructor
public class IRedisAspect {

    private final IRedis iRedis;

    @Pointcut("@annotation(threadJedis)")
    public void pointCut(ThreadJedis threadJedis) {
    }


    @Around("pointCut(threadJedis)")
    public Object redisAround(ProceedingJoinPoint point, ThreadJedis threadJedis) throws Throwable {
        Object result;
        try {
            // 调用目标方法
            result = point.proceed();
        } catch (Throwable throwable) {
            // 异常通知
            throwable.printStackTrace();
            throw throwable;
        } finally {
            // 后置通知
            iRedis.closeJedis();
        }
        // 返回通知
        return result;
    }

    @Around("@annotation(threadShardJedis)")
    public Object redisAround(ProceedingJoinPoint point, ThreadShardJedis threadShardJedis) throws Throwable {
        Object result;
        try {
            // 调用目标方法
            result = point.proceed();
        } catch (Throwable throwable) {
            // 异常通知
            throwable.printStackTrace();
            throw throwable;
        } finally {
            // 后置通知
            iRedis.closeShardJedis();
        }
        // 返回通知
        return result;
    }
}

 


注解:

切面注解(这里使用的是注解模式切入):

/**
 * 注意:此注解慎用,若切面代理失效,则会造成 ThreadLocal 空间不释放
 * Jedis 切面注解
 * 使用注意:只能是代理对象调用时生效,类内部调用将无效必须手动关闭 ShardJedis
 */
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ThreadJedis {

}
/**
 * 注意:此注解慎用,若切面代理失效,则会造成 ThreadLocal 空间不释放
 * ShardJedis 切面注解
 * 使用注意:只能是代理对象调用时生效,类内部调用将无效必须手动关闭Jedis
 */
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ThreadShardJedis {
}

启动注解,只需要在主启动类中加入 @EnableBraineexRedis 即可开启 Redis 使用

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(value = {IRedisConfig.class})
public @interface EnableCarleaRedis {

}

主配置文件 application.yml  中需要配置引入自定的的配置文件,

spring:
  profiles:
    include: 
    - redis

 


使用:

private final IRedis iRedis;

/**
 * 此函数禁止不允许代理失效,否则切面无法关闭 Jedis,@ThreadJedis 谨慎使用
 */

@Override
@ThreadJedis
public DomainTag domainById(String id) {
    Jedis jedis = iRedis.getJedis();
    // 。。。。。
    // 无需申明关闭资源,代理切面会帮我们关闭
}

 

上一篇:使用Jedis实现手机验证码功能


下一篇:Java 连接 Redis