• SpringCache实战遇坑


    1. SpringCache实战遇坑

    1.1. pom

    1. 主要是以下两个
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- 配合redis做缓存 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    

    1.2. Redis配置

    package com.zhiyis.common.config;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cache.CacheManager;
    import org.springframework.cache.annotation.CachingConfigurerSupport;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.cache.interceptor.KeyGenerator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
    import redis.clients.jedis.JedisPoolConfig;
    
    import java.lang.reflect.Method;
    
    
    @Configuration
    @EnableCaching
    public class RedisConfig extends CachingConfigurerSupport {
    
        private static Logger logger = LoggerFactory.getLogger(RedisConfig.class);
    
        @Value("${spring.redis.host}")
        private String redisHost;
    
        @Value("${spring.redis.port}")
        private int redisPort;
    
        @Value("${spring.redis.timeout}")
        private int redisTimeout;
    
        @Value("${spring.redis.password}")
        private String redisAuth;
    
        @Value("${spring.redis.database}")
        private int redisDb;
    
        @Value("${spring.redis.pool.max-active}")
        private int maxActive;
    
        @Value("${spring.redis.pool.max-wait}")
        private int maxWait;
    
        @Value("${spring.redis.pool.max-idle}")
        private int maxIdle;
    
        @Value("${spring.redis.pool.min-idle}")
        private int minIdle;
    
        @Bean
        @Override
        public KeyGenerator keyGenerator() {
            return new KeyGenerator() {
                @Override
                public Object generate(Object target, Method method, Object... params) {
                    StringBuilder sb = new StringBuilder();
                    sb.append(target.getClass().getName());
                    sb.append(method.getName());
                    for (Object obj : params) {
                        sb.append(obj.toString());
                    }
                    return sb.toString();
                }
            };
        }
    
        @Bean
        public CacheManager redisCacheManager() {
            RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate());
            //默认300秒过期
            cacheManager.setDefaultExpiration(300);
            // 启动时加载远程缓存
            cacheManager.setLoadRemoteCachesOnStartup(true);
            //是否使用前缀生成器
            cacheManager.setUsePrefix(true);
            return cacheManager;
        }
    
    
        @Bean
        public RedisConnectionFactory redisConnectionFactory() {
            JedisPoolConfig poolConfig = new JedisPoolConfig();
            poolConfig.setMaxTotal(maxActive);
            poolConfig.setMaxIdle(maxIdle);
            poolConfig.setMaxWaitMillis(maxWait);
            poolConfig.setMinIdle(minIdle);
            poolConfig.setTestOnBorrow(true);
            poolConfig.setTestOnReturn(false);
            poolConfig.setTestWhileIdle(true);
            JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(poolConfig);
            jedisConnectionFactory.setPassword(redisAuth);
            jedisConnectionFactory.setHostName(redisHost);
            jedisConnectionFactory.setDatabase(redisDb);
            jedisConnectionFactory.setPort(redisPort);
            jedisConnectionFactory.setTimeout(redisTimeout);
            return jedisConnectionFactory;
        }
    
        @Bean
        public RedisTemplate<String, Object> redisTemplate() {
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            Jackson2JsonRedisSerializer<Object> serializer = jackson2JsonRedisSerializer();
            redisTemplate.setConnectionFactory(redisConnectionFactory());
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(serializer);
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(serializer);
            return redisTemplate;
        }
    
        @Bean
        public Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
            final Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
            final ObjectMapper objectMapper = Jackson2ObjectMapperBuilder
                    .json().build();
            objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
            return jackson2JsonRedisSerializer;
        }
    }
    
    

    在application.properties填上相应的参数

    1.3. 使用

    1.3.1. 坑1

    1. 目前主要使用的就是缓存和删除缓存
    @Cacheable(sync = true, value = "on_hospital_list", key = "'3003101006_'+#requestReport.body['carer_id']", condition = "#requestReport.body['carer_id'] !=  '' ")
        @Override
        public ResponseReport getHospitalList(RequestReport requestReport) {
            ResponseReport responseReport = new ResponseReport();
           。。。
            return responseReport.returnSuccessResult(hospitals, "获取医院列表成功", requestReport);
        }
    
    1. 这里没有经验的人可能会纠结很久,因为我封装的入参对象,里面放的是JSONObject或者map作为的body值,这里我一开始是写成requestReport.body.carer_id这样的,但是这样会报如下错误
    EL1008E: object of type 'com.alibaba.fastjson.JSONObject' - maybe not public
    

    但你在网上找答案,都是文不对题,或者说其他错误导致相同的报错,反正我是找不到正确的解答
    3. 解决方法就是如上代码,直接写成#requestReport.body['carer_id']

    1.3.2. 坑2

    1. 删除缓存,我自定义了一个注解,原因是好像CacheEvict没提供删除多个key的方法
    //        @CacheEvict(value = "on_hospital_list", key="'3003101006_'+#requestReport.body['carer_id']")
        @CacheRemove(value = "on_hospital_list"/*,key={"'3003101006_'+#requestReport.body['carer_id']","'3003101007_'+#requestReport.body['carer_id']"}*/)
        @Override
        public ResponseReport upDownServer(RequestReport requestReport) {
         		 。。。业务逻辑
    
            return responseReport.returnError("9999", "上下线失败", requestReport);
        }
    
    1. 注解
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CacheRemove {
     
        /**
         * 需要清除的大类 例如 autocms 所有缓存
         *
         * @return
         */
        String value() default "";
     
     
        /**
         * 需要清除的具体的额类型
         *
         * @return
         */
        String[] key() default {};
    }
    
    
    1. 注解实现
    
    import com.zhiyis.framework.annotation.CacheRemove;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.expression.EvaluationContext;
    import org.springframework.expression.Expression;
    import org.springframework.expression.ExpressionParser;
    import org.springframework.expression.spel.standard.SpelExpressionParser;
    import org.springframework.expression.spel.support.StandardEvaluationContext;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    /**
     * 清除缓存切面类
     *
     * @author laoliangliang
     * @date 2019/1/14 16:04
     */
    @Component
    @Aspect
    public class CacheRemoveAspect {
    
        Logger logger = LoggerFactory.getLogger(this.getClass());
    
        @Autowired
        RedisTemplate<String, String> redis;
    
        ExpressionParser parser = new SpelExpressionParser();
        LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
    
        /**
         * 截获标有@CacheRemove的方法
         */
        @Pointcut(value = "(execution(* *.*(..)) && @annotation(com.zhiyis.framework.annotation.CacheRemove))")
        private void pointcut() {
        }
    
        /**
         * 功能描述: 切面在截获方法返回值之后
         */
        @AfterReturning(value = "pointcut()")
        private void process(JoinPoint joinPoint) {
            Object[] args = joinPoint.getArgs();
            //获取切入方法的数据
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            //获取切入方法
            Method method = signature.getMethod();
            //获得注解
            CacheRemove cacheRemove = method.getAnnotation(CacheRemove.class);
    
    		//注解解析
            String[] params = discoverer.getParameterNames(method);
            EvaluationContext context = new StandardEvaluationContext();
            for (int len = 0; len < params.length; len++) {
                context.setVariable(params[len], args[len]);
            }
    
    
            if (cacheRemove != null) {
                StringBuilder sb = new StringBuilder();
                String value = cacheRemove.value();
                if (!value.equals("")) {
                    sb.append(value);
                }
                //需要移除的正则key
                String[] keys = cacheRemove.key();
                sb.append(":");
                for (String key : keys) {
                    Expression expression = parser.parseExpression(key);
                    String value1 = expression.getValue(context, String.class);
                    //指定清除的key的缓存
                    cleanRedisCache(sb.toString() + value1);
                }
            }
    
        }
    
        private void cleanRedisCache(String key) {
            if (key != null) {
                //删除缓存
                redis.delete(key);
                logger.info("清除 " + key + " 缓存");
            }
        }
    }
    
    
    1. 这里的注解写入参数,如果想要使用spel表达式,要写上解析注解的一段代码

    参考

    https://huan1993.iteye.com/blog/2395239

    http://www.cnblogs.com/imyijie/p/6518547.html

  • 相关阅读:
    关于 Profile
    empty
    Vim Editor
    C++ Note
    Android NDK Sample
    Dealing with the ! when you import android project
    File attributes and Authority of Linux
    Java与C的相互调用
    The source code list of Android Git project
    Enable Android progurad in the case of multiple JAR lib
  • 原文地址:https://www.cnblogs.com/sky-chen/p/10268229.html
Copyright © 2020-2023  润新知