项目中有些业务方法希望在有缓存的时候直接从缓存获取,不再执行方法,来提高吞吐率。而且这种情况有很多。如果为每一个方法都写一段if else的代码,导致耦合非常大,不方便后期的修改。

思来想去,决定使用自动注解+Spring AOP来实现。

直接贴代码。

自定义注解类:

package com.ns.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;
/**
*
* ---------
* @author Han
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisCached {
/**
* redis key
* @return
*/
String value();
/**
* 过期时间,默认为1分钟,如果要设置永不过期,请设置小于等于0的值
* @return
*/
long timeout() default 1;
/**
* 时间单位,默认为分钟
* @return
*/
TimeUnit timeunit() default TimeUnit.MINUTES;
/**
* 额外定义一个空方法,调用该方法来对之前的缓存进行更新
* @return
*/
boolean forDelete() default false;
}

这个注解方便我们标志那个方法需要作为AOP的切入点。

AOP实现:

package com.ns.redis.aop;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit; import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.hibernate.metamodel.binding.Caching;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component; import com.ns.annotation.RedisCached;
import com.ns.redis.dao.base.BaseRedisDao;
/**
* 对dao的getbean的缓存处理
* order保证优先执行此切面
* @author Han
*/
@Aspect
public class AutoRedisCached extends BaseRedisDao<Object, Object> implements Ordered{
private static final Logger log = LoggerFactory.getLogger(RedisLockAspect.class);
/*
* 约束任意包下的包含Dao的类的任意方法,并且被cached注解
*/
@Pointcut("execution(* *..*(..)) && @annotation(com.ns.annotation.RedisCached)")
private void cacheMethod(){} @Around("cacheMethod()")
public Object doArround(ProceedingJoinPoint pjp) throws Throwable{
Object[] args = pjp.getArgs(); MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
Method method = methodSignature.getMethod();
final RedisCached cacheinfo = method.getAnnotation(RedisCached.class); //定义序列化器
final RedisSerializer<String> keySerializer = getStringSerializer();
final RedisSerializer valueSerializer = new Jackson2JsonRedisSerializer(method.getReturnType()); //序列化参数,作为hashkey
byte [] keyBytesTemp = keySerializer.serialize(cacheinfo.value());
for(Object arg : args){
keyBytesTemp = ArrayUtils.addAll(keyBytesTemp, getDefaultSerializer().serialize(arg));
}
//取md5后key
final byte [] keyBytes = keySerializer.serialize(DigestUtils.md5Hex(keyBytesTemp)); //是删除方法
if(cacheinfo.forDelete()){
execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
return connection.del(keyBytes);
}
});
return null;
} Object obj= null;
log.info("方法"+method.getName()+"切面,尝试从缓存获取...");
obj = execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
byte [] tmp = connection.get(keyBytes);
return valueSerializer.deserialize(tmp);
}
});
if(obj == null){
log.info("方法"+method.getName()+"切面,缓存未找到...");
final Object objReturn = pjp.proceed();
if(objReturn != null){
execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
if(cacheinfo.timeout() > 0){
connection.setEx(keyBytes, TimeUnit.SECONDS.convert(cacheinfo.timeout(), cacheinfo.timeunit()), valueSerializer.serialize(objReturn));
}else{
connection.set(keyBytes,valueSerializer.serialize(objReturn));
}
return true;
}
});
}
obj = objReturn;
}else{
log.info("方法"+method.getName()+"切面,缓存命中...");
}
//从dao获取
return obj;
} @Override
public int getOrder() {
return -1;
}
}

注:Orderd接口是为了保证此代码优先于其他切面执行

深入理解Spring Redis的使用 (八)、Spring Redis实现 注解 自动缓存的更多相关文章

  1. redis学习(八)——redis应用场景

    毫无疑问,Redis开创了一种新的数据存储思路,使用Redis,我们不用在面对功能单调的数据库时,把精力放在如何把大象放进冰箱这样的问题上,而是利用Redis灵活多变的数据结构和数据操作,为不同的大象 ...

  2. Redis笔记(八)Redis的持久化

    Redis相比Memcached的很大一个优势是支持数据的持久化, 通常持久化的场景一个是做数据库使用,另一个是Redis在做缓存服务器时,防止缓存失效. Redis的持久化主要有快照Snapshot ...

  3. Spring Boot2 系列教程(八)Spring Boot 中配置 Https

    https 现在已经越来越普及了,特别是做一些小程序或者公众号开发的时候,https 基本上都是刚需了. 不过一个 https 证书还是挺费钱的,个人开发者可以在各个云服务提供商那里申请一个免费的证书 ...

  4. redis之(八)redis的有序集合类型的命令

    [一]增加元素 --->命令:ZADD key score member [score member] --->向有序集合放入一个分数为score的member元素 --->元素存在 ...

  5. Redis集群(八):Redis Sharding集群

    一.Redis目前的集群方案主要有两种:Redis Sharding和Redis Cluster 1.Redis Sharding:3.0以前基本上使用分片实现集群,目前主流方案,客户端实现 2.Re ...

  6. redis(十八):Redis 配置

    #redis.conf# Redis configuration file example.# ./redis-server /path/to/redis.conf ################# ...

  7. Spring操作指南-IoC基础环境配置(基于注解自动装配)

    项目源码:http://code.taobao.org/p/LearningJavaEE/src/LearningSpring001%20-%20Automatically%20wiring%20be ...

  8. Redis实战(八)Redis的配置文件介绍

    https://www.cnblogs.com/ysocean/p/9074787.html

  9. spring boot / cloud (十八) 使用docker快速搭建本地环境

    spring boot / cloud (十八) 使用docker快速搭建本地环境 在平时的开发中工作中,环境的搭建其实一直都是一个很麻烦的事情 特别是现在,系统越来越复杂,所需要连接的一些中间件也越 ...

随机推荐

  1. shell 其他杂项知识点笔记

    // 其他杂项知识点 对于普通用户,Base shell 默认的提示符是美元符号$:对于超级用户(root 用户),Bash Shell 默认的提示符是井号# ------- \#!是一个约定的标记, ...

  2. Jenkins编辑或替换All view

    为什么我不能编辑“All”view? 这是因为它的类型是“All”而不是“List”,并且“All”类型是不可编辑的.你只能有一个“All”类型的view. 如果你想编辑这个View,你将不得不创建一 ...

  3. VMware虚拟机安装WIN7

    VMware在IT工作人员的学习之中,使用的较多,故聊一聊VMware中WIN7的安装: 第一步:安装VMware,这个软件百度就可以下载,但是是收费软件,注册码可以百度到. 第二步:VMware安装 ...

  4. dataGridView笔记

    最近用dataGridView比较多,先把代码备份在这里,有时间系统总结一下 using FindId.DAL; using FindId.Model; using System; using Sys ...

  5. UOJ#7. 【NOI2014】购票 点分治 斜率优化 凸包 二分

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ7.html 题解 这题是Unknown的弱化版. 如果这个问题出在序列上,那么显然可以CDQ分治 + 斜率 ...

  6. Python之tkinter:调用python库的tkinter带你进入GUI世界(一)——Jason niu

    #tkinter应用案例五:Label组件设图片为背景并点击按钮触发事件 from tkinter import * from PIL.ImageTk import PhotoImage from s ...

  7. Linux-day1-上课笔记

    命令的组成 命令关键字 [选项] [参数] 注意: 1. 通常情况下 选项- --连接 ls -l /etc 2. - 选项和选项之间是可以合并的 ls -ld /etc ls 罗列文件   常见的选 ...

  8. Mac安装sqlite3

    1,下载最新版本的 Mac OS X 会预安装 SQLite,但是如果没有可用的安装,只需按照如下步骤进行: 去安装最新的3.8.6版本,和linux的sqlite3安装版本是同一个. 软件地址为:h ...

  9. 记一次非常规方法对接硬件设备(Grason Stadler GSI 61)

    Grason Stadler GSI 61 电测听设备 (写下设备的名字, 希望别人遇坑可以搜到) 对接说明 设备厂家提供自带的软件,但是没有找到接口说明.我们需要获取设备发送过来的数据. 厂家提供的 ...

  10. BZOJ 3864

    dp of dp 我就是来贴个代码 #include<bits/stdc++.h> using namespace std; #define rep(i,a,b) for(int i=(a ...