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

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

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

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

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

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

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

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

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

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

/**
 * 缓存数据
 * @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连不上了,连不上就要执行业务的查询方法,不能返回空数据给用户。

@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, 比如说我查询用户张三的数据,缓存起来了,下次再查询张三的,才能把缓存的数据直接返回给用户,不然你查个赵四的也返回张三的那不就有问题了吗。

//采用访问的类加上访问的方法名加上参数值作为唯一的key
String key = className + "_" + methodName + "_" + params;

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

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

@RequestMapping("/users")
@Cache(timeOut=30, timeUnit=TimeUnit.SECONDS)
public ResponseData queryUsers() {
    return ResponseData.ok(userService.queryUsers());
}

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

原文发布于微信公众号 - 猿天地(cxytiandi)

原文发表时间:2016-12-06

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏你不就像风一样

Jsoup模拟登录带验证码的教务系统(原理详解)

在模拟登陆该教务系统时,笔者观察到该教务系统还有一个不需要验证码即可登陆的网址:http://jwxt.qlu.edu.cn/jsxsd/xsxk/xklc_l...

1572
来自专栏开发与安全

linux网络编程之socket(一):socket概述和字节序、地址转换函数

一、什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口。 socket不仅可以用于本机的进程间通信,还可以用于网络上不同主机的进程...

3170
来自专栏IT杂记

通过Java程序提交通用Mapreduce无法回收类的问题

问题描述 上次发布的博客 通过Java程序提交通用Mapreduce,在实施过程中发现,每次提交一次Mapreduce任务,JVM无法回收过程中产生的MapRe...

3106
来自专栏纯洁的微笑

Guava 源码分析(Cache 原理【二阶段】)

在上文「Guava 源码分析(Cache 原理)」中分析了 Guava Cache 的相关原理。

1811
来自专栏hotqin888的专栏

bootstrap treeview 增删改的正确姿势

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hotqin888/article/det...

4693
来自专栏IT杂记

通过Java程序提交通用Mapreduce任务并获取Job信息

背景 我们的一个业务须要有对MR任务的提交和状态跟踪的功能,须要通过Java代码提交一个通用的MR任务(包括mr的jar、配置文件、依赖的第三方jar包),并且...

1.1K5
来自专栏安恒网络空间安全讲武堂

PWNCTF部分复现

根据readData和writedata函数的逻辑发现数组是char [22][12],主要是判断越界的if语句有逻辑漏洞

2172
来自专栏开发技术

Cassandra-java操作——基本操作

  接着上篇博客,我们来谈谈java操作cassandra; 上篇博客的环境:jdk1.7 + python2.7.10 + cassandra2.2.8; 由...

1172
来自专栏haifeiWu与他朋友们的专栏

造个轮子之基于 Netty 实现自己的 RPC 框架

服务端开发都会或多或少的涉及到 RPC 的使用,当然如果止步于会用,对自己的成长很是不利,所以楼主今天本着知其然,且知其所以然的精神来探讨一下 RPC 这个东西...

1563
来自专栏MasiMaro 的技术博文

Windows平台下的内存泄漏检测

在C/C++中内存泄漏是一个不可避免的问题,很多新手甚至有许多老手也会犯这样的错误,下面说明一下在windows平台下如何检测内存泄漏。 在windows平...

2332

扫码关注云+社区

领取腾讯云代金券