前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用aop加redis实现通用接口缓存

用aop加redis实现通用接口缓存

作者头像
猿天地
发布2018-04-03 12:41:34
1.7K0
发布2018-04-03 12:41:34
举报
文章被收录于专栏:猿天地猿天地

系统在高并发场景下,最有用的三个方法是缓存,限流,降级。

缓存就是其中之一,目前缓存基本上是用redis或者memcached。

redis和memcached的优势,劣势在哪里,这里就不细说了,各位看官自行研究。

对于一些不经常更新的数据,比如说热门文章等等类的数据, 做缓存能减少数据库的压力。

其实做缓存也简单,在查询地方判断有没有缓存,没有就读数据库,然后缓存结果,有就直接读缓存,返回结果。

这样做没问题,问题是需要开发人员去关心每个方法,都要写一遍判断,不够通用。

于是有人说,能不能像别的框架一样,加个注解,就自动对这个接口做缓存呢?

完全可以啊,今天介绍下用aop + 注解 + redis来实现统一的缓存功能。

首先我们可以定义一个用于缓存的注解,有缓存的时间,时间的单位属性。

代码语言:javascript
复制
/**
 * 缓存数据
 * @author yinjihuan
 *
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cache {
    /**
     * 过期时间
     * @return
     */
    public abstract long timeOut() default 0;
    /**
     * 过期单位
     * @return
     */
    public abstract TimeUnit timeUnit() default TimeUnit.HOURS;
}

然后呢就是写一个切面,切到所有接口,在执行业务方法前判断是否有缓存,然后进行对应的操作。下面贴出部分代码。

下面操作redis的地方都要捕获异常,防止redis连不上了,连不上就要执行业务的查询方法,不能返回空数据给用户。

代码语言:javascript
复制
@Around("execution(* com.cxytiandi.spring_redis.controller.*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();
    //是否要缓存
    boolean isCache = false;
    //缓存时间
    Long timeOut = 0L;
    //缓存单位
    TimeUnit timeUnit = null;
    //访问的类对象
    Class<?> cls = joinPoint.getTarget().getClass();
    //正在执行的方法名
    String methodName = joinPoint.getSignature().getName();
    Map<String, Object> map = isCache(cls, methodName);
    isCache = (boolean) map.get("isCache");
    if(isCache){
        timeOut = (long) map.get("timeOut");
        timeUnit = (TimeUnit) map.get("timeUnit");
        //获取执行方法的参数列表
        Object[] args = joinPoint.getArgs(); 
        //得到所有参数的值
        StringBuffer params = new StringBuffer();
        try {
            getCallMethodParams(args, params);
        } catch (Exception e) {
            logger.error("", e);
        }

        String className = cls.getSimpleName();
        //采用访问的类加上访问的方法名加上参数值作为唯一的key
        String key = className + "_" + methodName + "_" + params;
        logger.info("Cache key:"+key);

        try {
            if(stringRedisTemplate.hasKey(key)){
                try {
                    return queryCache(start, methodName, key);
                } catch (Exception e) {
                    logger.error("获取redis的缓存数据异常", e);
                }
            } else {
                Object result = joinPoint.proceed();
                try {
                    setCache(timeOut, timeUnit, key, result);
                } catch (Exception e) {
                    logger.error("缓存数据到redis异常", e);
                }
                return result;
            }
        } catch (Exception e) {
            logger.error("", e);
        }
    }
    return joinPoint.proceed();
}

不知道大家有没有看到这行代码,这边要产生一个唯一的key,用来标识某次查询请求

这里用到了类名+方法名然后再加上查询的参数值做为key, 比如说我查询用户张三的数据,缓存起来了,下次再查询张三的,才能把缓存的数据直接返回给用户,不然你查个赵四的也返回张三的那不就有问题了吗。

代码语言:javascript
复制
//采用访问的类加上访问的方法名加上参数值作为唯一的key
String key = className + "_" + methodName + "_" + params;

其实代码没多少,主要就是思路要理解清楚就ok。

用的地方就简单了,要缓存就加注解。

代码语言:javascript
复制
@RequestMapping("/users")
@Cache(timeOut=30, timeUnit=TimeUnit.SECONDS)
public ResponseData queryUsers() {
    return ResponseData.ok(userService.queryUsers());
}

源码下载:http://cxytiandi.com/code/detail/29

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-12-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 猿天地 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档