美文网首页
2018-09-24

2018-09-24

作者: 毛子果 | 来源:发表于2018-09-24 12:31 被阅读0次

    springboot中AOP+redis封装缓存


    目录

    一、目的
    二、配置
    --1、pom.xml
    --2、redis配置
    --3、响应类
    --4、AOP切面的配置
    三、如何使用自已写的缓存程序


    一、目的

    在controller层的方法上加一个注释就可以利用aop切面去做代码的扩展,在代码的扩展中利用redis做为缓存中间件。关于注解就用自定义的@RedisCached


    二、配置

    1、pom.xml
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    2、redis配置

    基础配置

    @Configuration
    public class RedisConfig {
        /**
         * @Description: 创建一个模板类,将redis连接工厂设置到模板类中{ @link RedisTemplate}
         * @Author: maozi
         * @Date: 2018/6/5 11:49
         * @see:
         **/
        @Bean
        public RedisTemplate redisTemplate(RedisConnectionFactory factory){
            RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
            template.setConnectionFactory(factory);
            return template;
        }
    }
    

    再封装一层

    @Repository
    public class RedisDao {
    
        /**
         * @Description: 字符类的模板{ @link }
         * @Author: maozi
         * @Date: 2018/6/5 11:52
         * @see:
         **/
        @Autowired
        private StringRedisTemplate stringTemplate;
    
        /**
         * @Description: 对象类的模板 { @link }
         * @Author: maozi
         * @Date: 2018/6/5 11:53
         * @see:
         **/
        @Autowired
        private RedisTemplate<String, Object> template;
    
        public void setStringKey(String key, String value,int expire) {
            if(stringTemplate.hasKey(key)){
                stringTemplate.delete(key);
            }
    
            ValueOperations<String, String> ops = stringTemplate.opsForValue();
            ops.set(key,value,expire, TimeUnit.MINUTES);
        }
    
        public String getStringValue(String key) {
            ValueOperations<String, String> ops = this.stringTemplate.opsForValue();
            return ops.get(key);
        }
    
        public Object getValue(String key) {
            return template.opsForValue().get(key);
        }
    
        public void setKey(String key,Object value,long minutes){
            if(template.hasKey(key)){
                template.delete(key);
            }
            template.opsForValue().set(key,value,minutes, TimeUnit.MINUTES);
        }
    
        public boolean existByKey(String key){
            return template.hasKey(key);
        }
    
        public boolean existByStringKey(String key){
            return stringTemplate.hasKey(key);
        }
    
        public long getExpireTime(String key){
            return template.getExpire(key);
        }
    
        public Boolean setExpireToReturnYes(String key,long timeout){
            if (!template.hasKey(key)){
                return false;
            }
            return template.expire(key,timeout,TimeUnit.MINUTES);
        }
    
        public Boolean setStringExpireToReturnYes(String key,long timeout){
            if(!stringTemplate.hasKey(key)){
                return false;
            }
            return stringTemplate.expire(key,timeout,TimeUnit.MINUTES);
        }
    
        public void cleanCacheByString(String key){
            stringTemplate.delete(key);
        }
    
        public void cleanCache(String key){
            template.delete(key);
        }
    }
    

    注意:这里的模板有分字符的和对象的,如果value是字符的,那建议有字符模板

    application.properties

    # redis
    spring.redis.host=localhost
    spring.redis.port=6379
    spring.redis.password=
    spring.redis.database=1
    spring.redis.pool.max-active=8
    spring.redis.pool.max-wait=-1
    spring.redis.pool.max-idle=500
    spring.redis.pool.min-idle=0
    spring.redis.timeout=0
    
    3、响应类
    @ApiModel(description = "请求返回结果")
    public class ResponseResult {
        private String errorCode;
        private String errorMsg;
        private Object objectResult;
    
    
        public String getErrorCode() {
            return errorCode;
        }
    
        public void setErrorCode(String errorCode) {
            this.errorCode = errorCode;
        }
    
        public String getErrorMsg() {
            return errorMsg;
        }
    
        public void setErrorMsg(String errorMsg) {
            this.errorMsg = errorMsg;
        }
    
        public Object getObjectResult() {
            return objectResult;
        }
    
        public void setObjectResult(Object objectResult) {
            this.objectResult = objectResult;
        }
    
        @Override
        public String toString(){
            return JSON.toJSONString(this);
        }
    
    }
    
    4、AOP切面的配置

    自定义注解

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface RedisCached {
        public int expire() default 5;//过期时间,默认5分钟
    }
    

    切面类

    /**
     * @Description: redis缓存的AOP
     * @Author: maozi
     * @Date: 2018/9/3 10:07
     * @see: RedisCached
     **/
    @Aspect
    @Component
    public class RedisAspect {
    
        private final Logger logger = LoggerFactory.getLogger(RedisAspect.class);
    
        @Autowired
        RedisDao redisDao;
    
        @Pointcut("execution(public com.zhengjia.entity.ResponseResult com.zhengjia.web.*.*(..)) && @annotation(com.zhengjia.common.Annotation.RedisCached)")
        public void redisAdvice(){}
    
        @Around("redisAdvice()")
        public Object Interceptor(ProceedingJoinPoint pjp){
            Object result = null;
            RequestAttributes ra = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes sra = (ServletRequestAttributes) ra;
            HttpServletRequest request = sra.getRequest();
    
            String port = String.valueOf(request.getServerPort());
            String uri = request.getRequestURI();
            String methodType = request.getMethod();
            String queryString = request.getQueryString();
    
            //反射拿方法信息
            Method method=getMethod(pjp);
    
            //获得annotation的信息
            RedisCached annotation = method.getAnnotation(RedisCached.class);
            int expire = annotation.expire();
    
            //检查请求类型
            if(!methodType.equalsIgnoreCase("GET")){
                throw new RuntimeException("只允许get请求做缓存");
            }
    
            //key的唯一性由url加上参数保证
            String keyName = "";
            if(method.getAnnotation(IgnoreToken.class) == null){ //如果没有注解的话,keyName规则上加上一sourceFrom参数
                String token = request.getHeader("Authorization");
                if(token.isEmpty()) throw new RuntimeException("请求头Authorization中没有token信息");
                Map userMap = (Map)redisDao.getValue(token);
                keyName = (String)userMap.get("sourceFrom") + port + uri + "?" + queryString;
            }else {
                keyName = port + uri + "?" + queryString;
            }
    
            ResponseResult responseResult = new ResponseResult();
    
            try {
                if (!redisDao.existByKey(keyName)){//没存在缓存,去查数据库
                    result = pjp.proceed();
                    responseResult = (ResponseResult)result;
                    if(responseResult.getObjectResult() != null){ //防止缓存空值
                        redisDao.setKey(keyName,responseResult.getObjectResult(),expire);
                    }
                }else{//存在缓存中,在缓存中取数据库
                    responseResult.setObjectResult(redisDao.getValue(keyName));
                    responseResult.setErrorCode(Constants.RESPONSE_CODE_SUCCESS);
                    responseResult.setErrorMsg(Constants.RESPONSE_MSG_OK);
                    result = responseResult;
                    logger.info("redis缓存中查询数据,key值为" + keyName);
                }
    
            } catch (Throwable e) {
                e.printStackTrace();
                responseResult.setErrorCode(Constants.RESPONSE_CODE_ERROR);
                responseResult.setErrorMsg("redisAspect报错!");
                logger.info("redisAspect报错!");
            }
            return result;
        }
    
        /**
         *  获取被拦截方法对象
         *
         *  MethodSignature.getMethod() 获取的是顶层接口或者父类的方法对象
         *  而缓存的注解在实现类的方法上
         *  所以应该使用反射获取当前对象的方法对象
         */
        public Method getMethod(ProceedingJoinPoint pjp){
            //获取参数的类型
            Object [] args=pjp.getArgs();
            Class [] argTypes=new Class[pjp.getArgs().length];
            if(args.length == 1 && args[0] == null){
                args = new Object[0];
            }
            for(int i=0;i<args.length;i++){
                argTypes[i]=args[i].getClass();
            }
            Method method=null;
            try {
                //有参的情况下
                if(args.length > 0){
                    method = pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(), argTypes);
                }else { //无参的情况下
                    Class clazz = Class.forName(pjp.getSignature().getDeclaringTypeName());
                    Method[] i = clazz.getMethods();
                    for(Method data : clazz.getMethods()){
                        if(data.getName().equalsIgnoreCase(pjp.getSignature().getName())){
                            method = data;
                            break;
                        }
                    }
    
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return method;
    
        }
    }
    

    注意:
    1、缓存的key的唯一性是用url来保证,而且这里只允许Get请求,如果想扩展,那就自行修改代码,另外key的唯一性也可以用其他方式来确定。
    2、这里缓存ResponseResult 中的objectResult的值,所以要用ResponseResult做返回,另外是因为,返回值在切面中做了限制。也就是说不用ResponseResult做返回值返回,那这个缓存是不起作用的。
    3、expire是提供出去,动态设置缓存过期的时间


    三、如何使用自已写的缓存程序

    /**
      * 商圈-购物中心关联度
      */
    @GetMapping(value = "/correlation-degree/{name}")
    @RedisCached(expire = 6)
    public ResponseResult shopMallCorrelationDegree(@PathVariable String name) {
        ResponseResult responseResult = new ResponseResult();
    
        List<Map<String, Object>> resultList = bussinessCircleService.getShopMallCorrelationDegree(name);
    
        responseResult.setErrorCode(Constants.RESPONSE_CODE_SUCCESS);
        responseResult.setErrorMsg(Constants.RESPONSE_MSG_OK);
        responseResult.setObjectResult(resultList);
        return responseResult;
    }
    

    相关文章

      网友评论

          本文标题:2018-09-24

          本文链接:https://www.haomeiwen.com/subject/puvcoftx.html