利用AOP和注解的方式实现redis的数据缓存
代码链接
之前一直没有用到redis,最近想学习一下redis,那么首先想到的就是将数据库的结果添加到缓存中,那么下次访问的时候如果命中缓存了就可以不用访问数据库,节省了时间。
我在网上搜索了几篇文章,发现他们都是在每个业务逻辑里面添加缓存判断,伪代码如下:
1 2 3 4 5 6 7 8 9
| public Object method1(Object param1){ if(redis has cache){ return redis result; } Object dbResult = dao.select(); redis.add(dbResult); return dbResult }
|
如果这样写,那么在每个需要缓存的地方都需要添加与本身业务无关的代码,对代码的侵入比较大。所以我利用aop和注解实现了一个方法,在需要缓存的地方添加该注解就可以实现缓存,不会对代码有侵入。最终实现调用的结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Override @EnableRedisCache(Key = "user", Time = 100000) public ResultBean getUserById(Long id) { return ResultUtil.success(userDao.selectById(id)); }
@Override public ResultBean getUserNoCache(Long id) { return ResultUtil.success(userDao.selectById(id)); }
|
该实现主要是利用了aop原理,通过对EnableRedisCache
注解进行拦截,如果有该注解就进入到拦截方法中。
使用@interface
即可声明一个注解,@Target({ElementType.METHOD})
表示要用在方法上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface EnableRedisCache {
String Key() default "";
long Time() default 1000L;
TimeUnit TIME_UNIT() default TimeUnit.MILLISECONDS;
}
|
然后实现对该注解的拦截:
由于我之前没有调用过redis的api,所以闹出了一个问题,我想设置在redis中存储的时间时调用了operations.set(key,val,time)
这个方法,我进入这个方法看了一眼也没有仔细看,以为这个就是调用了默认的时间单位设置过期时间。结果这样调用后不行了,进入redis查看数据也不对。就很奇妙。经朋友提现调用的方法不对,需要调用的是operations.set(key,val,time,time_unit)
这样的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| @Aspect @Component public class RedisAspect {
@Autowired private RedisTemplate redisTemplate; @Around("@annotation(enableRedisCache)") public Object around(ProceedingJoinPoint proceedingJoinPoint, EnableRedisCache enableRedisCache) { MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature(); Method method = signature.getMethod(); String className = proceedingJoinPoint.getTarget().getClass().getName(); String methodName = signature.getName(); LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer(); String[] paramNames = u.getParameterNames(method); Object[] args = proceedingJoinPoint.getArgs(); String key = enableRedisCache.Key(); String redisKey = className + methodName + key; if (args != null && paramNames != null) { for (int i = 0; i < args.length; i++) { redisKey += paramNames[i] + ":" + args[i]; } } long cacheTime = enableRedisCache.Time(); TimeUnit timeUnit = enableRedisCache.TIME_UNIT(); Object result = getCacheByRedisKey(proceedingJoinPoint, redisKey, cacheTime, timeUnit); return result; }
private Object getCacheByRedisKey(ProceedingJoinPoint proceedingJoinPoint, String redisKey, long cacheTime, TimeUnit timeUnit) { ValueOperations<String, Object> operations = redisTemplate.opsForValue(); try { if (redisTemplate.hasKey(redisKey)) { ResultBean cacheResult = (ResultBean) operations.get(redisKey); if (cacheResult == null) { return null; } System.out.println("通过缓存获取数据"); return cacheResult; } else { ResultBean dbResult = (ResultBean) proceedingJoinPoint.proceed(); System.out.println("通过数据库获取数据"); operations.set(redisKey, dbResult, cacheTime, timeUnit); return dbResult; } } catch (Exception e) { e.printStackTrace(); } catch (Throwable throwable) { throwable.printStackTrace(); } return null; }
}
|
这里只有查询的方法,后面添加更新和删除方法时需要将存储到redis中的key(这里用了类名、方法名等拼接)进行修改,不然执行更新和删除时不方便找的key。