如何通过spring-aop的方式自定义注解来实现spring-cache的功能
发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,本篇文章给大家分享的是有关如何通过spring-aop的方式自定义注解来实现spring-cache的功能,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小
千家信息网最后更新 2026年11月02日如何通过spring-aop的方式自定义注解来实现spring-cache的功能
本篇文章给大家分享的是有关如何通过spring-aop的方式自定义注解来实现spring-cache的功能,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
设计的过程中参考一下几个原则:
代码无侵入
按需加载
配置多样化
首先自定义注解:只能作用于方法上,运行期有效,key支持spel表达式,其中FunctionEnum是根据业务自定义的一个枚举
@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface GlobalCache { /** * SPEL表达式,缓存key * @return */ String key() default ""; String value() default ""; /** * 当前具体的操作 * eg:信息新增,删除等 * * @return */ FunctionEnum functionEnum() default FunctionEnum.DEFAULT;}通过定义aop的切面来解析当前这个注解,核心实现如下
@Around("@annotation(com.xxx.xxxx.core.foreign.annotation.GlobalCache)") public Object globalCacheAround(ProceedingJoinPoint joinPoint) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); ServiceData result; GlobalCache globalCache = method.getAnnotation(GlobalCache.class); String cacheKey = globalCache.functionEnum().name() + ":" + ForeignUtils.combineParam(methodSignature.getParameterNames(),joinPoint.getArgs(),globalCache.key(),String.class,"test-spel-123"); if ("GET".equals(request.getMethod())) { result = defaultRedisTemplate.opsForValue().get(cacheKey); if (result != null) { log.info("命中缓存,缓存的key:{}", cacheKey); return result; } } Map httpParams = ForeignUtils.builtParams(methodSignature.getParameterNames(), request); result = CompletableFuture.supplyAsync(() -> { //重定向相关操作 return redirectUrl(cacheKey,request,httpParams,ForeignUtils.getRequestBody(method,joinPoint)); }, LocalThreadPool.FOREIGN_EXEC_CACHE).whenComplete((serviceData, ex) -> { if (ex == null) { //本地缓存的业务处理 notice.onSuccess(globalCache, httpParams, serviceData); } else { notice.onError(ex, serviceData); throw new ForeignInvokeException("current request was deny..."); } }).join(); return result; } 在构造的过程中遇到很多小的零碎的问题,其中涉及到如何解析PUT请求中的body等等,下面附上ForeignUtils工具类的代码
/** * 组装缓存key * 格式: 方法名:参数值1:参数值2 * 自动解析格式 * * @param functionName 当前操作对应的名称 * @param args 所有变量对应的参数 * @return */ public static String combineParameter(String functionName, Method method, Object[] args) { Class[] classArr = method.getParameterTypes(); for (int index = 0; index < classArr.length; index++) { if (classArr[index] == HttpServletRequest.class) { //是否存在其他待忽略的? continue; } functionName += ":" + args[index]; } return functionName; } /** * 请求参数的参数名称和参数对应的值 key参数:value参数变量 * title:test-123,subtitle:test-subtitle-123 * * @param params * @param request * @return */ public static Map builtParams(String[] params, HttpServletRequest request) { Map keyMap = Maps.newHashMap(); for (int i = 0; i < params.length; i++) { String value = request.getParameter(params[i]); if (StringUtils.isNotBlank(value)) { keyMap.put(params[i], value); } } return keyMap; } /** * 拼装http后请求参数,占位符的方式 * title={title}&subtitle={subtitle} * 可以使用queryString()替代 * * @param httpParams * @return */ public static String builtHttpParams(Map httpParams) { String result = ""; for (Map.Entry entry : httpParams.entrySet()) { result += entry.getKey() + "= {" + entry.getKey() + "}&"; } if (result.endsWith("&")) { return result.substring(0, result.length() - 1); } return result; } /** * 获取当前请求中的body值 * * @param method * @param joinPoint * @return */ public static Object getRequestBody(Method method, ProceedingJoinPoint joinPoint) { Annotation[][] currentAnnotionArr = method.getParameterAnnotations(); Object body = null; for (int index = 0; index < currentAnnotionArr.length; index++) { try { if (currentAnnotionArr[index][0].annotationType() == RequestBody.class) { body = joinPoint.getArgs()[index]; break; } } catch (Exception e) { } } return body; } /** * 获取请求中path的参数对 * @param method * @param joinPoint * @return */ public static String getPathArgs(Method method, ProceedingJoinPoint joinPoint) { Annotation[][] currentAnnotionArr = method.getParameterAnnotations(); String pathValue = null; for (int index = 0; index < currentAnnotionArr.length; index++) { try { if (currentAnnotionArr[index][0].annotationType() == PathVariable.class) { pathValue = String.valueOf(joinPoint.getArgs()[index]); break; } } catch (Exception e) { } } return pathValue; } private static ExpressionParser parser = new SpelExpressionParser(); /** * 解析SPEL表达式 缓存对应key信息 * * @param params * @param args * @param spel * @param clazz * @param defaultResult * @return */ public static T combineParam(String[] params, Object[] args, String spel, Class clazz, T defaultResult) { EvaluationContext context = new StandardEvaluationContext(); for (int index = 0; index < params.length; index++) { context.setVariable(params[index], args[index]); } try { Expression expression = parser.parse_Expression(spel); return expression.getValue(context, clazz); } catch (Exception e) { return defaultResult; } } 上面的工具类主要涉及到参数的组装,解析等;
以上就是如何通过spring-aop的方式自定义注解来实现spring-cache的功能,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注行业资讯频道。
参数
缓存
注解
方式
表达式
功能
业务
代码
信息
变量
名称
工具
方法
更多
格式
知识
篇文章
过程
实用
有效
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
中智农互联网科技
互联网科技公司起名
盈实互联网科技
朗新科技与工业互联网
互联网中国科技
远拓互联网科技
互联网科技上市公司
互联网科技美容
上海亿帐通互联网科技
互联网科技股有哪些
宁波青枫互联网科技有限公司
互联网和黑科技的看法
互联网科技公司实习生
浙南科技城互联网大厦招聘
世界互联网科技成果奖
中国新注册互联网科技
云南互联网科技学费
广州三三互联网科技有限公司
互联网科技博主张洲
10位科技互联网大佬
互联网科技工作委员会主任
中国互联网科技工作委员会毛军
科技伦理 互联网公司
江苏南京365互联网科技
深圳前海聚择互联网科技有限公司
链环科技传统企业怎么转型互联网
互联网时代的科技公司
互联网高科技行业有哪些企业
米泉互联网科技专业怎么样
盐城市攀科互联网科技有限公司