• spring cache redis


    一、使用开源包(spring-data-redis

    1、引入jar包

    <dependency>
         <groupId>org.springframework.data</groupId>
         <artifactId>spring-data-redis</artifactId>
         <version>1.8.11.RELEASE</version>
     </dependency>

    2、配置Spring xml文件

    <!--  结合现在配置 -->

    <bean name="redisTemplate" class=" RedisTemplate">
           <property name="redisPool" ref="redisPool"/>
            <property name="keySerializer" ref="org.springframework.data.redis.serializer.StringRedisSerializer"></property>
           <property name="hashKeySerializer" ref="org.springframework.data.redis.serializer.StringRedisSerializer"></property>
    </bean>


    <bean name="cacheManager" class="com.config.ExtendedRedisCacheManager">
        <constructor-arg name="redisOperations" ref="redisTemplate"/>
        <constructor-arg name="cacheNames">
            <set>
                <value>default</value>
                <value>test1</value>
            </set>
        </constructor-arg>
        <!-- 默认缓存名字 -->
     <property name="defaultCacheName" value="default"/>
        <!-- 是否在容器启动时初始化 -->
     <property name="loadRemoteCachesOnStartup" value="true"/>
        <!-- 是否使用前缀 -->
     <property name="usePrefix" value="true"/>
        <!-- 前缀命名,仅当usePrefix为true时才生效 -->
     <property name="cachePrefix">
            <bean class="org.springframework.data.redis.cache.DefaultRedisCachePrefix">
                <constructor-arg name="delimiter" value=":"/>
            </bean>
        </property>
        <!-- 缓存名字和有效期的分隔符 -->
     <property name="separator" value="#"/>
        <!-- 默认有效期60 -->
     <property name="defaultExpiration" value="60"/>
    </bean>

    ps:redis配置继续用现有的

    3、配置redis挂掉,直接访问数据库(redis挂掉,最好配置预警)

    @Configuration
    @EnableCaching
    public class AppCacheConfigurer extends CachingConfigurerSupport {
        private Logger logger = LoggerFactory.getLogger(AppCacheConfigurer.class);

        public class AppCacheErrorHandler implements CacheErrorHandler {
            @Override
     public void handleCacheGetError(RuntimeException e, Cache cache, Object o) {
                if (e instanceof JedisConnectionException || e instanceof RedisConnectionFailureException) {
                    logger.warn("redis has lose connection:", e);
                    return;
                }
                throw e;
            }

            @Override
     public void handleCachePutError(RuntimeException e, Cache cache, Object o, Object o1) {
                if (e instanceof JedisConnectionException || e instanceof RedisConnectionFailureException) {
                    logger.warn("redis has lose connection:", e);
                    return;
                }
                throw e;
            }

            @Override
     public void handleCacheEvictError(RuntimeException e, Cache cache, Object o) {
                throw e;
            }

            @Override
     public void handleCacheClearError(RuntimeException e, Cache cache) {
                throw e;
            }
        }

        @Override
     public CacheErrorHandler errorHandler() {
            return new AppCacheErrorHandler();
        }
    }

    5、配置独立设置过期时间,重写RedisCacheManager

    public class ExtendedRedisCacheManager extends RedisCacheManager {

        private static final Logger logger = Logger.getLogger(ExtendedRedisCacheManager.class);

        private static final ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("JavaScript");

        private static final Pattern pattern = Pattern.compile("[+\-*/%]");

        private String defaultCacheName;

        private char separator = '#';

        public ExtendedRedisCacheManager(RedisOperations redisOperations) {
            this(redisOperations, Collections.<String>emptyList());
        }

        public ExtendedRedisCacheManager(RedisOperations redisOperations, Collection<String> cacheNames) {
            super(redisOperations, cacheNames);
        }

        @Override
     public Cache getCache(String name) {
            // try to get cache by name
     RedisCache cache = (RedisCache) super.getCache(name);
            if (cache != null) {
                return cache;
            }

            // there's no cache which has given name
     // find separator in cache name
     int index = name.lastIndexOf(getSeparator());
            if (index < 0) {
                return null;
            }

            // split name by the separator
     String cacheName = name.substring(0, index);
            if(StringUtils.isBlank(cacheName)){
                cacheName = defaultCacheName;
            }
            cache = (RedisCache) super.getCache(cacheName);
            if (cache == null) {
                return null;
            }

            // get expiration from name
     Long expiration = getExpiration(name, index);
            if (expiration == null || expiration < 0) {
                logger.warn("Default expiration time will be used for cache '{}' because cannot parse '{}', cacheName : " + cacheName + ", name : " + name);
                return cache;
            }

            return new RedisCache(cacheName, (isUsePrefix() ? getCachePrefix().prefix(cacheName) : null), getRedisOperations(), expiration);
        }


        public char getSeparator() {
            return separator;
        }

        /**
     * Char that separates cache name and expiration time, default: #.
     *
     * @param separator
     */
     public void setSeparator(char separator) {
            this.separator = separator;
        }

        private Long getExpiration(final String name, final int separatorIndex) {
            Long expiration = null;
            String expirationAsString = name.substring(separatorIndex + 1);
            try {
                // calculate expiration, support arithmetic expressions.
     if(pattern.matcher(expirationAsString).find()){
                    expiration = (long) Double.parseDouble(scriptEngine.eval(expirationAsString).toString());
                }else{
                    expiration = Long.parseLong(expirationAsString);
                }
            } catch (NumberFormatException ex) {
                logger.error(String.format("Cannnot separate expiration time from cache: '%s'", name), ex);
            } catch (ScriptException e) {
                logger.error(String.format("Cannnot separate expiration time from cache: '%s'", name), e);
            }

            return expiration;
        }

        @Override
     public void setUsePrefix(boolean usePrefix) {
            super.setUsePrefix(usePrefix);
        }

        @Override
     public void setCachePrefix(RedisCachePrefix cachePrefix) {
            super.setCachePrefix(cachePrefix);
        }

        public void setDefaultCacheName(String defaultCacheName) {
            this.defaultCacheName = defaultCacheName;
        }
    }

     

    6、通过注解设置缓存

     

    @Cacheable(value = "test1#60",key = "#p0")//Cacheable会判断key是否存在,增加redis缓存,设置为60秒,key为test1:xxx

    @CachePut(cacheNames="test1#60",key = "#p0")//CachePut不会判断key是否存在,直接覆盖,增加redis缓存,设置为60秒,key为test1:xxx,它每次都会触发真实方法的调用

    @CacheEvict(value = "test1",key = "#p1")//删除缓存

    其他缓存注解,参考spring cache注解相关定义

     

    二、解决现有系统版本不兼容问题

    1、线上spring版本为:3.2.9.RELEASE

         解决兼容redis挂掉的spring版本为4.1以上

        适用于现在系统的配置如下

    2、引入jar包

    <dependency>
         <groupId>org.springframework.data</groupId>
         <artifactId>spring-data-redis</artifactId>
         <version>1.3.4.RELEASE</version>
     </dependency>

    PS:低版本主要为了兼容现在的spring版本和jedis版本

    3、实现自己的Spring cache,主要是为了兼容现有redis配置(现有系统redis的实现是自己实现的)

    public class ExtendRedisCache implements Cache {


        private static final Logger LOG = LoggerFactory.getLogger(ExtendRedisCache.class);

        /**
     * Redis
     */
     private RedisTemplate redisTemplate;

        /**
     * 缓存名称
     */
     private String name;

        /**
     * 默认超时时间
     */
     private int timeout;

        @Override
     public String getName() {
            return this.name;
        }

        @Override
     public Object getNativeCache() {
            return this.redisTemplate;
        }

        @Override
     public ValueWrapper get(Object key) {

            if (StringUtils.isEmpty(key)) {
                return null;
            }
            Jedis jedis = null;
            try{
                jedis = redisTemplate.getRedisPool().getResource();
                //键
     final String finalKey = getKey(key);

                if(StringUtils.isEmpty(finalKey)){
                    return null;
                }

                byte[] value = jedis.get(finalKey.getBytes());
                if (value == null) {
                    return null;
                }

                Object object = SerializableUtil.unserialize(value);
                return (object != null ? new SimpleValueWrapper(object) : null);

            }catch (Exception e){
                LOG.error("redis has lose connection:",e);
                return null;
            }finally {
                if (null != jedis) {
                    jedis.close();
                }
            }

        }

        /**
     * 获取redis缓存键
     *
     * @param key
     * @return
     */
     private String getKey(Object key) {
            //键
     final String finalKey;

            if (key instanceof String) {
                finalKey = this.name + ":" + (String) key;
            } else {
                finalKey = this.name + ":" + key.toString();
            }

            return finalKey;
        }

        @Override
     public void put(Object key, Object value) {

            if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
                return;
            }

            Jedis jedis = null;
            try{
                //键
     final String finalKey = getKey(key);

                if(!StringUtils.isEmpty(finalKey)){
                    jedis = redisTemplate.getRedisPool().getResource();
                    redisTemplate.set(finalKey.getBytes(), SerializableUtil.serialize(value));
                    jedis.expire(finalKey.getBytes(),timeout);
                }
            }catch (Exception e){
                LOG.error("redis has lose connection:",e);
            }finally {
                if (null != jedis) {
                    jedis.close();
                }
            }


        }

        /*
     * 根据Key 删除缓存
     */
     @Override
     public void evict(Object key) {
            if (null != key) {
                try{
                    final String finalKey = getKey(key);

                    if (!StringUtils.isEmpty(finalKey)) {
                        redisTemplate.del(finalKey.getBytes());
                    }
                }catch (Exception e){
                    LOG.error("redis has lose connection:",e);
                }
            }
        }

        /*
     * 清除系统缓存
     */
     @Override
     public void clear() {
            //线上不允许删除所有缓存
     }

        public RedisTemplate getRedisTemplate() {
            return redisTemplate;
        }

        public void setRedisTemplate(RedisTemplate redisTemplate) {
            this.redisTemplate = redisTemplate;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getTimeout() {
            return timeout;
        }

        public void setTimeout(int timeout) {
            this.timeout = timeout;
        }
    }

    4、添加spring xml配置

     

    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:cache="http://www.springframework.org/schema/cache"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/cache
    http://www.springframework.org/schema/cache/spring-cache.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.2.xsd ">

    PS:添加红色标注地方

    <!-- 启用Spring对基于注解的Cache的支持 -->
    <cache:annotation-driven/>

    <!-- 缓存管理器 -->
    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <bean class="ExtendRedisCache">

                    <property name="redisTemplate" ref="redisTemplate" /> //现有系统的redisTemplate

                    <property name="name" value="test1" /> //缓存名(结合key)
                    <property name="timeout" value="60" /> //过期时间
                </bean>
            </set>
        </property>
    </bean>

    5、通过注解设置缓存

    @Cacheable(value = "test1",key = "#p0")//Cacheable会判断key是否存在,增加redis缓存,key为test1:xxx,这里过期时间在xml里配置

    @CachePut(cacheNames="test1",key = "#p0")//CachePut不会判断key是否存在,直接覆盖,它每次都会触发真实方法的调用,增加redis缓存,key为test1:xxx,,这里过期时间在xml里配置

    @CacheEvict(value = "test1",key = "#p1")//删除缓存

    其他缓存注解,参考spring cache注解相关定义

  • 相关阅读:
    Elastic Search查询DSL的生成器
    清除Git仓库未托管的文件
    在Asp.net Core 中配置HTTPS支持
    VUE3的新构建工具Vite使用简介
    文档驱动 —— 表单组件(六):基于AntDV的Form表单的封装,目标还是不写代码
    文档驱动 —— 查询组件:使用 vue3.0 的新特性,重构代码
    文档驱动 —— 表单组件(五):基于Ant Design Vue 的表单控件的demo,再也不需要写代码了。
    文档驱动 —— 表单组件(四):基于Ant Design Vue封装一些表单域控件
    文档驱动 —— 表单组件(三):基于原生html的表单组件demo
    文档驱动 —— 表单组件(二):meta生成器,告别书写代码
  • 原文地址:https://www.cnblogs.com/haifg/p/9711375.html
Copyright © 2020-2023  润新知