使用AOP实现+自定义注解 实现 缓存
一般做一个缓存处理,我们正常的做法是在需要缓存的方法中。
查询缓存是否存在,不存在,查询(DB或远程接口),如果数据存在,那么则缓存,否则下次记录查询。
当我们做1、2、3个方法这样写还行,如果有10、20个方法需要缓存,这样是不是就很繁琐了。。
所以,我们需要一个标签来做缓存,当然springcache提供有缓存的标签。还有各种策略。
但是,我就是要自己写一个。。
这个处理的方式原理就是通过AOP拦截自己定义的标签,然后通过标签中的参数,决定缓存多久,加标签的就做缓存。
单独几个的写法如下:
@Autowired private RedisTemplate redisTemplate; /**查询数据*/ public Object selectValue(String dicCode, String key) { String redisKey = "selectValue"+dicCode+key; Object result = redisTemplate.opsForValue().get(redisKey); if (null == result) { result = dictionaryDao.getDicInfoValue(dicCode, key); if (null != result) { redisTemplate.opsForValue().set(redisKey, result, 30, TimeUnit.SECONDS); } } return result; }
当我们做了几个后,修改成AOP+自定义注解
自定义注解:RedisCacheAT.java
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @desc:缓存注解-redis * @author 陈惟鲜 * @date 2023年5月10日 下午7:36:28 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface RedisCacheAT { /**缓存时间,默认60秒*/ public int times() default 60; }
AOP切面:
RedisCacheAOP.java
import java.util.concurrent.TimeUnit; import javax.annotation.Resource; 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.springframework.core.annotation.Order; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component;/** * @Desc : 本地缓存方法,方法上加上@LocalCache即可 * @Company : 晨创科技 * @author : Chenweixian 陈惟鲜 * @Date : 2021年5月7日 下午7:47:56 */ @SuppressWarnings({"rawtypes","unchecked"}) @Aspect @Component @Order(8) // @Order(1)数字越小优先级越高 public class RedisCacheAspect { @Resource private RedisTemplate redisTemplate; /**拦截所有controller包下的方法*/ @Pointcut("@annotation(com.ccjr.core.logcollection.annotation.RedisCacheAT)") private void myMethod(){ } /** * 日志打印 * @author : 陈惟鲜 chenweixian * @Date : 2022年8月8日 下午5:29:47 * @param point * @return * @throws Throwable */ @Around("myMethod() && @annotation(cache)") public Object doAround(ProceedingJoinPoint point, RedisCacheAT cache) throws Throwable { String class_name = point.getTarget().getClass().getName(); String method_name = point.getSignature().getName(); String requestStr = MyAopUtils.getRequestParam(point); String cacheKey = class_name + "." + method_name + requestStr;
// 防止key过长,直接取md5值 cacheKey = "redisCache:"+MD5Utils.getMD5(cacheKey); Object result = redisTemplate.opsForValue().get(cacheKey); if (null == result) { result = point.proceed();// result的值就是被拦截方法的返回值 if (null != result) { redisTemplate.opsForValue().set(cacheKey, result, cache.times(), TimeUnit.SECONDS); } } return result; } }
其中工具类获取请求参数
import org.apache.commons.lang.ArrayUtils; import org.aspectj.lang.ProceedingJoinPoint; /** * 切面拦截工具 * @Company : 晨创科技 * @author : Chenweixian * @Date : 2022年11月15日 下午10:45:01 */ public class MyAopUtils { /** * 参数处理,超过指定长度字符的,只显示1000... * @author : 陈惟鲜 chenweixian * @Date : 2018年8月10日 上午11:44:11 * @param paramStr * @param strlength * @return */ public static String parameterHandle(String paramStr, int strlength){ if (paramStr.length() > strlength){ paramStr = paramStr.substring(0, strlength) + "..."; } if (paramStr.length() > 10){ paramStr = "[" + paramStr + "]"; } return paramStr; } /*** * 获取请求参数 * @author : 陈惟鲜 chenweixian * @Date : 2018年8月9日 下午3:47:08 * @param point * @return */ public static String getRequestParam(ProceedingJoinPoint point){ /** * 获取方法的参数值数组。 */ Object[] methodArgs = point.getArgs(); // 结果 String requestStr = ""; /** * 获取方法参数名称 */ try { requestStr = logParam(methodArgs); } catch (Exception e) { requestStr = "获取参数失败"; } return requestStr; } // /** // * 使用javassist来获取方法参数名称 // * @param class_name 类名 // * @param method_name 方法名 // * @return // * @throws Exception // */ // private String[] getFieldsName(String class_name, String method_name) throws Exception { // Class<?> clazz = Class.forName(class_name); // String clazz_name = clazz.getName(); // ClassPool pool = ClassPool.getDefault(); // ClassClassPath classPath = new ClassClassPath(clazz); // pool.insertClassPath(classPath); // // CtClass ctClass = pool.get(clazz_name); // CtMethod ctMethod = ctClass.getDeclaredMethod(method_name); // MethodInfo methodInfo = ctMethod.getMethodInfo(); // CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); // LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); // if(attr == null){ // return null; // } // String[] paramsArgsName = new String[ctMethod.getParameterTypes().length]; // int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1; // for (int i=0;i<paramsArgsName.length;i++){ // paramsArgsName[i] = attr.variableName(i + pos); // } // return paramsArgsName; // } /** * 判断是否为基本类型:包括String * @param clazz clazz * @return true:是; false:不是 */ private static boolean isPrimite(Class<?> clazz){ if (clazz.isPrimitive() || clazz == String.class){ return true; }else { return false; } } /** * 打印方法参数值 基本类型直接打印,非基本类型需要重写toString方法 * @param paramsArgsName 方法参数名数组 * @param paramsArgsValue 方法参数值数组 */ private static String logParam(Object[] paramsArgsValue){ if(ArrayUtils.isEmpty(paramsArgsValue)){ return ""; } StringBuffer buffer = new StringBuffer(); for (int i=0;i<paramsArgsValue.length;i++){ //参数名 // String name = paramsArgsName[i]; //参数值 Object value = paramsArgsValue[i]; // buffer.append(name +" = "); if(isPrimite(value.getClass())){ buffer.append(value + " ,"); }else { buffer.append(value.toString() + " ,"); } } return buffer.toString(); } }
修改后,原来的方法,自定义标签使用后,就很简单了。
/**查询数据*/ @RedisCacheAT public Object selectValue(String dicCode, String key) { Object result = dictionaryDao.getDicInfoValue(dicCode, key); return result; }
完。。。。。。。。。。。。。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器