转载请注明出处:https://blog.csdn.net/l1028386804/article/details/82597154

使用Cacheable注解Redis方法时,如果Redis服务器挂了,就直接抛出异常了,
java.net.ConnectException: Connection refused: connect

那么,有没有什么办法可以继续向下执行方法,从相关的数据库中查询数据,而不是直接抛出异常导致整个程序终止运行呢?

经过反复翻看Spring的源码和相关资料,并经过不断验证,得出了答案:有相关的方案!!!

解决方案就是重写CachingConfigurerSupport中的四个方法:

package org.springframework.cache.annotation;

import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.interceptor.KeyGenerator;

/**
 * An implementation of {@link CachingConfigurer} with empty methods allowing
 * sub-classes to override only the methods they're interested in.
 *
 * @author Stephane Nicoll
 * @since 4.1
 * @see CachingConfigurer
 */
public class CachingConfigurerSupport implements CachingConfigurer {

	@Override
	public CacheManager cacheManager() {
		return null;
	}

	@Override
	public KeyGenerator keyGenerator() {
		return null;
	}

	@Override
	public CacheResolver cacheResolver() {
		return null;
	}

	@Override
	public CacheErrorHandler errorHandler() {
		return null;
	}

}

具体如下:

1、BaseRedisConfig类

package io.mykit.cache.redis.spring.annotation.config;

import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;

/**
 * @author liuyazhuang
 * @version 1.0.0
 * @date 2018/8/21 18:48
 * @description Redis集群配置基础类
 */
@Data
public class BaseRedisConfig extends CachingConfigurerSupport {

    @Value("${redis.cluster.max.total}")
    protected Integer maxTotal;

    @Value("${redis.cluster.max.idle}")
    protected Integer maxIdle;

    @Value("${redis.cluster.min.idle}")
    protected Integer minIdle;

    @Value("${redis.cluster.timeout}")
    protected Integer timeout;

    @Value("${redis.cluster.maxAttempts}")
    protected Integer maxAttempts;

    @Value("${redis.cluster.redisDefaultExpiration}")
    protected Integer redisDefaultExpiration;

    @Value("${redis.cluster.usePrefix}")
    protected Boolean usePrefix;

    @Value("${redis.cluster.blockWhenExhausted}")
    protected Boolean blockWhenExhausted;

    @Value("${redis.cluster.maxWaitMillis}")
    protected Integer maxWaitMillis;

    @Value("${redis.cluster.testOnBorrow}")
    protected Boolean testOnBorrow;

    @Value("${redis.cluster.testOnReturn}")
    protected Boolean testOnReturn;

    @Value("${redis.cluster.testWhileIdle}")
    protected Boolean testWhileIdle;

    @Value("${redis.cluster.minEvictableIdleTimeMillis}")
    protected Integer minEvictableIdleTimeMillis;

    @Value("${redis.cluster.timeBetweenEvictionRunsMillis}")
    protected Integer timeBetweenEvictionRunsMillis;

    @Value("${redis.cluster.numTestsPerEvictionRun}")
    protected Integer numTestsPerEvictionRun;

    @Value("${redis.cluster.password}")
    protected String password;

    @Value("${redis.cluster.defaultExpirationKey}")
    protected String defaultExpirationKey;

    @Value("${redis.cluster.expirationSecondTime}")
    protected Integer expirationSecondTime;

    @Value("${redis.cluster.preloadSecondTime}")
    protected Integer preloadSecondTime;

    @Value("${redis.cluster.index.zero}")
    protected Integer zero;

    @Value("${redis.cluster.index.one}")
    protected Integer one;

    @Value("${redis.cluster.index.two}")
    protected Integer two;

    @Value("${redis.cluster.index.three}")
    protected Integer three;

    @Value("${redis.cluster.node.one}")
    protected String nodeOne;

    @Value("${redis.cluster.node.one.port}")
    protected Integer nodeOnePort;

    @Value("${redis.cluster.node.two}")
    protected String nodeTwo;

    @Value("${redis.cluster.node.two.port}")
    protected Integer nodeTwoPort;

    @Value("${redis.cluster.node.three}")
    protected String nodeThree;

    @Value("${redis.cluster.node.three.port}")
    protected Integer nodeThreePort;

    @Value("${redis.cluster.node.four}")
    protected String nodeFour;

    @Value("${redis.cluster.node.four.port}")
    protected Integer nodeFourPort;

    @Value("${redis.cluster.node.five}")
    protected String nodeFive;

    @Value("${redis.cluster.node.five.port}")
    protected Integer nodeFivePort;

    @Value("${redis.cluster.node.six}")
    protected String nodeSix;

    @Value("${redis.cluster.node.six.port}")
    protected Integer nodeSixPort;

    @Value("${redis.cluster.node.seven}")
    protected String nodeSeven;

    @Value("${redis.cluster.node.seven.port}")
    protected Integer nodeSevenPort;

    @Override
    public String toString(){
        return JSONObject.toJSONString(this);
    }

}

2、CacheRedisConfig类

package io.mykit.cache.redis.spring.annotation.config;

import com.alibaba.fastjson.parser.ParserConfig;
import io.mykit.cache.redis.spring.aspect.CachingAnnotationsAspect;
import io.mykit.cache.redis.spring.cache.CacheKeyGenerator;
import io.mykit.cache.redis.spring.cache.CacheTime;
import io.mykit.cache.redis.spring.cache.CustomizedRedisCacheManager;
import io.mykit.cache.redis.spring.serializer.FastJsonRedisSerializer;
import io.mykit.cache.redis.spring.serializer.StringRedisSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import redis.clients.jedis.JedisPoolConfig;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author liuyazhuang
 * @version 1.0.0
 * @date 2018/8/21 19:06
 * @description Redis配置类
 */
@Slf4j
public class CacheRedisConfig extends BaseRedisConfig{

    /**
     * 配置 JedisPoolConfig
     * @return JedisPoolConfig对象
     */
    @Bean
    public JedisPoolConfig jedisPoolConfig(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(maxTotal);
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMinIdle(minIdle);
        jedisPoolConfig.setBlockWhenExhausted(blockWhenExhausted);
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
        jedisPoolConfig.setTestOnBorrow(testOnBorrow);
        jedisPoolConfig.setTestOnReturn(testOnReturn);
        jedisPoolConfig.setTestWhileIdle(testWhileIdle);
        jedisPoolConfig.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        jedisPoolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
        return jedisPoolConfig;
    }

    /**
     * 配置 RedisClusterConfiguration
     * @return RedisClusterConfiguration对象
     */
    @Bean
    public RedisClusterConfiguration redisClusterConfiguration(){
        RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
        redisClusterConfiguration.setMaxRedirects(3);
        redisClusterConfiguration.setClusterNodes(getRedisNodes());
        return redisClusterConfiguration;
    }

    /**
     * 配置 JedisConnectionFactory
     * @return 返回JedisConnectionFactory对象
     */
    @Bean
    public JedisConnectionFactory jedisConnectionFactory(){
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisClusterConfiguration(), jedisPoolConfig());
        jedisConnectionFactory.setPassword(password);
        jedisConnectionFactory.setTimeout(timeout);
        return jedisConnectionFactory;
    }


    /**
     * 配置RedisTemplate
     * @return RedisTemplate对象
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory());
        StringRedisSerializer keySerializer = new StringRedisSerializer();
        FastJsonRedisSerializer<Object> valueSerializer = new FastJsonRedisSerializer<Object>();
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        redisTemplate.setKeySerializer(keySerializer);
        redisTemplate.setValueSerializer(valueSerializer);
        redisTemplate.setHashKeySerializer(keySerializer);
        redisTemplate.setHashValueSerializer(valueSerializer);
        return redisTemplate;
    }

    /**
     * 配置CacheAnnotationTime
     * @return CacheAnnotationTime对象
     */
    @Bean
    public CacheTime cacheTime(){
        CacheTime cacheTime = new CacheTime(expirationSecondTime, preloadSecondTime);
        return cacheTime;
    }

    /**
     * 配置 CacheAnnotationKeyGenerator
     * @return CacheAnnotationKeyGenerator对象
     */
    @Bean
    public CacheKeyGenerator cacheKeyGenerator(){
        CacheKeyGenerator cacheKeyGenerator = new CacheKeyGenerator();
        return cacheKeyGenerator;
    }

    /**
     * 配置SpringCachingAnnotationsAspect
     * @return SpringCachingAnnotationsAspect对象
     */
    @Bean
    public CachingAnnotationsAspect cachingAnnotationsAspect(){
        CachingAnnotationsAspect cachingAnnotationsAspect = new CachingAnnotationsAspect();
        return cachingAnnotationsAspect;
    }


    /**
     * 配置CustomizedAnnotationRedisCacheManager
     * @return CustomizedAnnotationRedisCacheManager对象
     */
    @Bean
    public CustomizedRedisCacheManager customizedRedisCacheManager(){
        CustomizedRedisCacheManager customizedAnnotationRedisCacheManager = new CustomizedRedisCacheManager(redisTemplate());
        customizedAnnotationRedisCacheManager.setDefaultExpiration(redisDefaultExpiration);
        customizedAnnotationRedisCacheManager.setUsePrefix(usePrefix);
        Map<String, CacheTime> map = new HashMap<String, CacheTime>();
        map.put(defaultExpirationKey, cacheTime());
        customizedAnnotationRedisCacheManager.setCacheTimes(map);
        return customizedAnnotationRedisCacheManager;
    }


    /**
     * 封装各Redis节点信息
     * @return Redis节点Set集合
     */
    private Set<RedisNode> getRedisNodes(){
        Set<RedisNode> set = new HashSet<RedisNode>();
        RedisNode redisNode1 = new RedisNode(nodeOne, nodeOnePort);
        set.add(redisNode1);

        RedisNode redisNode2 = new RedisNode(nodeTwo, nodeTwoPort);
        set.add(redisNode2);

        RedisNode redisNode3 = new RedisNode(nodeThree, nodeThreePort);
        set.add(redisNode3);

        RedisNode redisNode4 = new RedisNode(nodeFour, nodeFourPort);
        set.add(redisNode4);

        RedisNode redisNode5 = new RedisNode(nodeFive, nodeFivePort);
        set.add(redisNode5);

        RedisNode redisNode6 = new RedisNode(nodeSix, nodeSixPort);
        set.add(redisNode6);

        RedisNode redisNode7 = new RedisNode(nodeSeven, nodeSevenPort);
        set.add(redisNode7);
        return set;
    }

    @Override
    public CacheManager cacheManager() {
        return customizedRedisCacheManager();
    }

    @Override
    public KeyGenerator keyGenerator() {
        return cacheKeyGenerator();
    }

    @Override
    public CacheResolver cacheResolver() {
        return super.cacheResolver();
    }

    @Override
    public CacheErrorHandler errorHandler() {
        CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {

            @Override
            public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
                RedisErrorException(exception, key);
            }

            @Override
            public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
                RedisErrorException(exception, key);
            }

            @Override
            public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
                RedisErrorException(exception, key);
            }

            @Override
            public void handleCacheClearError(RuntimeException exception, Cache cache) {
                RedisErrorException(exception, null);
            }
        };
        return cacheErrorHandler;
    }

    protected void RedisErrorException(Exception exception,Object key){
        log.error("redis异常:key=[{}], exception={}", key, exception.getMessage());
    }
}

问题解决。