本次分享如何使用redis结合自定义注解实现基于方法的注解缓存,及托底缓存的实现思路

   现在的互联网公司大多数都是以Redis作为缓存,使用缓存的优点就不赘述了,写这篇文章的目的就是想帮助同学们如何在工作中更好的去实现缓存

目标

   在方法上使用注解,实现如果标注了注解的方法会优先走缓存,如果命中缓存则返回缓存中的数据,如果没有命中缓存就穿透到方法中执行方法,然后将方法的返回值存储到缓存中,然后下次就可以在缓存设置的有效时间内从缓存中读取数据了

实现步骤

  • 自定义注解

  • 定义aop切面

思路:

  • 如果方法标注了@CacheProfiler注解则走aop

  • 如果获取到CacheProfiler类,并且readFromCache()设置的是true,就去getCacheKey()获取缓存的key

  • 根据缓存的key值去redis中查询,如果有就查询缓存,如果没有就执行方法,将方法的返回值作为value存入缓存,并根据CacheProfiler的expire()设置的过期时间给key加上过期时间

自定义注解
  1. /**

  2. * @Description:

  3. * @author: chenmingyu

  4. * @date: 2018/5/17 20:37

  5. */

  6. @Retention(RetentionPolicy.RUNTIME)

  7. @Target({ElementType.METHOD})

  8. public @interface CacheProfiler {

  9.    String cacheKey();

  10.    int expire() default 60;

  11.    boolean readFromCache() default true;

  12. }

讲解下代码:

@interface来修饰一个注解

  • cacheKey() 缓存key的前缀,缓存key由key前缀+入参组成,详见getCacheKey()方法

  • expire() 缓存的过去时间,默认为60秒

  • readFromCache() 是否读取缓存,默认为true

定义aop切面
  1.    @Pointcut("@annotation(org.my.cache.annotation.CacheProfiler)")

  2.    public void cachePoint() {

  3.    }

  4.    @Around("cachePoint()")

  5.    public Object beforeExec(ProceedingJoinPoint joinPoint) {

  6.        Object obj = null;

  7.        try {

  8.            //获取方法

  9.            Method method = this.getMethod(joinPoint);

  10.            CacheProfiler cacheProfiler = (CacheProfiler) method.getAnnotation(CacheProfiler.class);

  11.            //方法上没有注解,直接执行方法然后返回

  12.            if (null == cacheProfiler) {

  13.                return joinPoint.proceed();

  14.            }

  15.            //缓存key

  16.            String cacheKey = this.getCacheKey(joinPoint, cacheProfiler);

  17.            //true:从缓存读

  18.            if (cacheProfiler.readFromCache()) {

  19.                obj = jedis.get(cacheKey);

  20.            } else {

  21.                return joinPoint.proceed();

  22.            }

  23.            if (null == obj) {

  24.                obj = joinPoint.proceed();

  25.            }else{

  26.                return obj;

  27.            }

  28.            if (null != obj) {

  29.                jedis.setex(cacheKey,cacheProfiler.expire(), JSONObject.toJSONString(obj));

  30.            }

  31.            return obj;

  32.        } catch (Throwable throwable) {

  33.            throwable.printStackTrace();

  34.    }

  35.        return obj;

  36.    }

讲解下代码:

@Pointcut定义切入点为@CacheProfiler注解 @Around表示使用@CacheProfiler注解的方法将走环绕通知 getMethod(JoinPoint jp)获取目标方法 getCacheKey(ProceedingJoinPoint jp, CacheProfiler cacheProfiler) 获取缓存的key

获取CacheProfiler的方法
  1.    //获取方法

  2.    private Method getMethod(JoinPoint jp) throws Exception {

  3.        MethodSignature msig = (MethodSignature) jp.getSignature();

  4.        Method method = msig.getMethod();

  5.        return method;

  6.    }

获取缓存的key的方法
  1. private String getCacheKey(ProceedingJoinPoint jp, CacheProfiler cacheProfiler) {

  2.        StringBuilder sb = new StringBuilder(cacheProfiler.cacheKey());

  3.        if (jp.getArgs() != null && jp.getArgs().length != 0) {

  4.            Object[] arr$ = jp.getArgs();

  5.            int len$ = arr$.length;

  6.            for (int i$ = 0; i$ < len$; ++i$) {

  7.                Object obj = arr$[i$];

  8.                if (obj != null) {

  9.                    sb.append("_").append(String.valueOf(obj));

  10.                }

  11.            }

  12.            return sb.toString();

  13.        } else {

  14.            return sb.toString();

  15.        }

  16.    }

   这个demo只是大概写了下,不过也可以实现本文的目标了,通过在方法上使用@CacheProfiler注解实现缓存,通过@CacheProfiler注解的相应参数去实现缓存属性的相关设置

写个Controller测试下
  1. /**

  2. * @Description:

  3. * @author: chenmingyu

  4. * @date: 2018/5/20 11:18

  5. */

  6. @RestController

  7. public class CacheController {

  8.    @RequestMapping("/getUser")

  9.    @CacheProfiler(cacheKey = "USER_CACHE_KEY",expire = 3*60,readFromCache = true)

  10.    public List<User> getUser(Integer type){

  11.        List<User> users = new ArrayList<>();

  12.        if(type==1){

  13.            User user = new User("明羽",24,"北京");

  14.            users.add(user);

  15.        }else if(type==2){

  16.            User user = new User("小娜",24,"北京");

  17.            users.add(user);

  18.        }

  19.        return users;

  20.    }

  21. }

以上就实现了注解缓存,


托底缓存的实现

   托底缓存的实现也很简单,首先说下可能需要托底缓存的场景,就比如一个电商网站,去获取商品列表,结果调用接口的时候出错了,这个时候又不希望网站的页面出现天窗【天窗的意思就是网站的页面上由于没获取到数据出现空白区域】,这个时候就需要托底数据了,即如果接口出现异常,也可以返回数据,【就是托底数据】

思路
  • 可以给CacheProfiler注解加一个属性是否读取托底缓存 属性为boolean

  • 然后在joinPoint.proceed()的时候加上try-cache,

  • 如果执行方法的时候报异常了,并且上面那个是否读取托底缓存属性为true,就去缓存中读取托底数据

  • 一般的代码中都会try-cache,如果在方法里已经捕获了异常,这就需要在执行的方法里去主动的抛异常,让aop切面可以捕获异常

  • 其中重要的一点就是托底数据什么时候去存,这个可以在每次去存缓存的时候去存一份托底数据,或者定义一些存储策略,托底数据与缓存的数据可以定义为key的前缀不同【其实这个可以存成hash类型的数据,定义一个固定的key为托底数据的key【hash的key】,然后field为缓存的key】

具体的代码就不写,有思路就好实现了,各位同学可以自己去实现

感觉写的还有点干货就关注我的公众号啊
不定期分享优质文章

redis结合自定义注解实现基于方法的注解缓存,及托底缓存的实现的更多相关文章

  1. springboot基于方法级别注解事务的多数据源切换问题

    springBoot多数据源配置 配置读数据源 @Component @ConfigurationProperties(prefix = "jdbc.read") @Propert ...

  2. Spring_day03--课程安排_基于aspectj的注解aop_Spring的jdbcTemplate操作

    Spring_day03 上节内容回顾 今天内容介绍 基于aspectj的注解aop Spring的jdbcTemplate操作 增加 修改 删除 查询 Spring配置c3p0连接池和dao使用jd ...

  3. 002-spring cache 基于声明式注解的缓存-02-CachePut、CacheEvict、Caching、CacheConfig、EnableCaching、自定义

    1.2.CachePut annotation 在支持Spring Cache的环境下,对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素 ...

  4. 在struts2.3.4.1中使用注解、反射、拦截器实现基于方法的权限控制

    权限控制是每一个系统都应该有的一个功能,有些只需要简单控制一下就可以了,然而有些却需要进行更加深入和细致的权限控制,尤其是对于一些MIS类系统,基于方法的权限控制就更加重要了. 用反射和自定义注解来实 ...

  5. 缓存篇~第七回 Redis实现基于方法签名的数据集缓存(可控更新,分布式数据缓存)

    返回目录 本篇文章可以说是第六回 Microsoft.Practices.EnterpriseLibrary.Caching实现基于方法签名的数据集缓存(可控更新,WEB端数据缓存)的续篇,事实上,有 ...

  6. Spring2.5那些事之基于AOP的方法级注解式日志配置

    在日常开发中经常需要在代码中加入一些记录用户操作日志的log语句,比如谁在什么时间做了什么操作,等等. 把这些对于开发人员开说无关痛痒的代码写死在业务方法中实在不是一件很舒服的事情,于是AOP应运而生 ...

  7. java反射机制获取自定义注解值和方法

    由于工作需求要应用到java反射机制,就做了一下功能demo想到这些就做了一下记录 这个demo目的是实现动态获取到定时器的方法好注解名称,废话不多说了直接上源码 1.首先需要自定义注解类 /** * ...

  8. 缓存篇~第八回 Redis实现基于方法签名的数据集缓存~续(优化缓存中的key)

    返回目录 上一讲主要是说如何将数据集存储到redis服务器里,而今天主要说的是缓存里的键名,我们习惯叫它key. redis或者其它缓存组件实现的存储机制里,它将很多方法对应的数据集存储在一个公共的空 ...

  9. Spring Security(17)——基于方法的权限控制

    目录 1.1     intercept-methods定义方法权限控制 1.2     使用pointcut定义方法权限控制 1.3     使用注解定义方法权限控制 1.3.1    JSR-25 ...

随机推荐

  1. Java不走弯路教程(6.JDBC)

    6.JDBC 在上一章,我们完成了MyDb数据库的简单的客户段调用.作为产品我们还封装了驱动程序,并且提供了统一的调用接口. 大家应该知道,市面上有多种数据库产品,比如Oracle,Mysql,DB2 ...

  2. 用js来实现那些数据结构14(树02-AVL树)

    在使用二叉搜索树的时候会出现 一个问题,就是树的一条分支会有很多层,而其他的分支却只有几层,就像下面这样: 如果数据量够大,那么我们在某条边上进行增删改查的操作时,就会消耗大量的时间.我们花费精力去构 ...

  3. JS核心笔记

    一.说明 JS权威指南文字用红色标出: JS高级程序设计用橙色标出; 自己加上的文字用粉红色标出: 其(一)-(九)为JS权指南,(十)为JS高级程序设计 二.记法结构 2.1字符集 Javascri ...

  4. WEB 集群与负载均衡(一)基本概念-上

    Web集群是由多个同时运行同一个web应用的服务器组成,在外界看来就像一个服务器一样,这多台服务器共同来为客户提供更高性能的服务.集群更标准的定义是:一组相互独立的服务器在网络中表现为单一的系统,并以 ...

  5. Web 前台优化

    大型网站--前端性能优化和规范 2013-10-28 09:00 by 贤达, 2769 阅读, 10 评论, 收藏, 编辑   Web性能涉及的范围太广,但一般web开发者在程序上线以后很多都曾遇到 ...

  6. Ibatis和Hibernate的比较

    Ibatis和Hibernate的比较 分类: IBATIS HIBERNATE2010-11-19 17:58 341人阅读 评论(0) 收藏 举报 hibernateibatis数据库sqlcac ...

  7. HP 3par多路径安装方法

    一.Linux下multipath介绍,需要以下工具包: 在CentOS 5中,最小安装系统时multipath已经被安装,查看multipath是否安装如下: 1.device-mapper-mul ...

  8. Mybatis 系列10

    在前九篇中,介绍了mybatis的配置以及使用, 那么本篇将走进mybatis的源码,分析mybatis 的执行流程 1. SqlSessionFactory 与 SqlSession. 通过前面的章 ...

  9. 百度技术沙龙之2013-2&3

    2013年2月2日技术沙龙 商业产品开发------谢马林 业务逻辑加大设计难度 集成难度大 降低学习成本 统一标准化开发模式 面向集成的架构平台 业务复杂,设计抽象的技术支撑不够 抽象6类数据流业务 ...

  10. 初探Apache Beam

    文章作者:luxianghao 文章来源:http://www.cnblogs.com/luxianghao/p/9010748.html  转载请注明,谢谢合作. 免责声明:文章内容仅代表个人观点, ...