当前位置: 首页 > news >正文

自定义注解facade 实现切面 进行日志记录和参数校验

目录

注解

切面

结论


注解

package cn.hollis.nft.turbo.rpc.facade;
/*** @author Hollis* FacadeAspect 类里的 facade 方法是一个 AOP(面向切面编程)环绕通知,* 它会拦截所有被 @cn.hollis.nft.turbo.rpc.facade.Facade* 注解标记的方法,* 对这些方法进行统一的参数校验、异常捕获和日志记录,增强服务方法的健壮性和可维护性。*/
public @interface Facade {
}

切面

package cn.hollis.nft.turbo.rpc.facade;import cn.hollis.nft.turbo.base.exception.BizException;
import cn.hollis.nft.turbo.base.exception.SystemException;
import cn.hollis.nft.turbo.base.response.BaseResponse;
import cn.hollis.nft.turbo.base.response.ResponseCode;
import cn.hollis.nft.turbo.base.utils.BeanValidator;
import com.alibaba.fastjson2.JSON;
import jakarta.validation.ValidationException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;/*** Facade的切面处理类,用于统一进行参数校验、异常捕获和日志记录,增强服务方法的健壮性和可维护性。** @author Hollis*/
@Aspect
@Component
public class FacadeAspect {/*** 日志记录器,用于记录切面处理过程中的相关信息。*/private static final Logger LOGGER = LoggerFactory.getLogger(FacadeAspect.class);/*** 环绕通知,拦截所有被 @cn.hollis.nft.turbo.rpc.facade.Facade 注解标记的方法,进行参数校验、异常捕获和日志记录。** @param pjp 连接点对象,包含目标方法的信息,可用于执行目标方法。* @return 目标方法的返回值或处理后的失败响应。* @throws Exception 可能抛出的异常,包括目标方法抛出的异常和切面处理过程中的异常。*/@Around("@annotation(cn.hollis.nft.turbo.rpc.facade.Facade)")public Object facade(ProceedingJoinPoint pjp) throws Exception {// 创建一个计时器,用于记录方法执行耗时StopWatch stopWatch = new StopWatch();stopWatch.start();// 获取目标方法对象Method method = ((MethodSignature) pjp.getSignature()).getMethod();// 获取目标方法的参数数组Object[] args = pjp.getArgs();// 记录方法开始执行的日志,包含方法名和参数信息LOGGER.info("start to execute , method = " + method.getName() + " , args = " + JSON.toJSONString(args));// 获取目标方法的返回类型Class returnType = ((MethodSignature) pjp.getSignature()).getMethod().getReturnType();// 循环遍历目标方法的所有参数,进行参数校验for (Object parameter : args) {try {// 使用 BeanValidator 工具类对参数进行校验BeanValidator.validateObject(parameter);} catch (ValidationException e) {// 参数校验失败,记录失败日志printLog(stopWatch, method, args, "failed to validate", null, e);// 返回通用的失败响应return getFailedResponse(returnType, e);}}try {// 执行目标方法,并获取返回值Object response = pjp.proceed();// 补全响应对象的信息,主要是 code 和 messageenrichObject(response);// 记录方法执行结束的日志printLog(stopWatch, method, args, "end to execute", response, null);return response;} catch (Throwable throwable) {// 目标方法执行过程中抛出异常,记录失败日志printLog(stopWatch, method, args, "failed to execute", null, throwable);// 返回通用的失败响应return getFailedResponse(returnType, throwable);}}/*** 打印方法执行相关的日志信息,使用统一的日志格式。** @param stopWatch 计时器,记录方法执行耗时。* @param method    目标方法对象。* @param args      目标方法的参数数组。* @param action    操作行为描述,如 "failed to validate"。* @param response  目标方法的响应对象。* @param throwable 可能抛出的异常对象。*/private void printLog(StopWatch stopWatch, Method method, Object[] args, String action, Object response,Throwable throwable) {try {// 调用 getInfoMessage 方法生成统一格式的日志信息,并记录日志LOGGER.info(getInfoMessage(action, stopWatch, method, args, response, throwable), throwable);} catch (Exception e1) {// 日志生成或记录过程中出现异常,记录错误日志LOGGER.error("log failed", e1);}}/*** 生成统一格式的日志信息,方便进行日志统计和监控。* 注意:如果调整此处的格式,需要同步调整日志监控配置。** @param action    操作行为描述。* @param stopWatch 计时器,记录方法执行耗时。* @param method    目标方法对象。* @param args      目标方法的参数数组。* @param response  目标方法的响应对象。* @param exception 可能抛出的异常对象。* @return 拼接后的统一格式的日志信息字符串。*/private String getInfoMessage(String action, StopWatch stopWatch, Method method, Object[] args, Object response,Throwable exception) {// 使用 StringBuilder 拼接日志信息StringBuilder stringBuilder = new StringBuilder(action);stringBuilder.append(" ,method = ");stringBuilder.append(method.getName());stringBuilder.append(" ,cost = ");stringBuilder.append(stopWatch.getTime()).append(" ms");// 如果响应对象是 BaseResponse 类型,添加 success 信息if (response instanceof BaseResponse) {stringBuilder.append(" ,success = ");stringBuilder.append(((BaseResponse) response).getSuccess());}// 如果存在异常,添加 success 为 false 的信息if (exception != null) {stringBuilder.append(" ,success = ");stringBuilder.append(false);}stringBuilder.append(" ,args = ");stringBuilder.append(JSON.toJSONString(Arrays.toString(args)));// 如果响应对象不为空,添加响应信息if (response != null) {stringBuilder.append(" ,resp = ");stringBuilder.append(JSON.toJSONString(response));}// 如果存在异常,添加异常信息if (exception != null) {stringBuilder.append(" ,exception = ");stringBuilder.append(exception.getMessage());}// 如果响应对象是 BaseResponse 类型且执行失败,添加执行失败信息if (response instanceof BaseResponse) {BaseResponse baseResponse = (BaseResponse) response;if (!baseResponse.getSuccess()) {stringBuilder.append(" , execute_failed");}}return stringBuilder.toString();}/*** 补全响应对象的信息,主要是设置 responseCode 和 responseMessage。* 如果响应对象是 BaseResponse 类型,根据 success 状态设置默认的 responseCode。** @param response 响应对象。*/private void enrichObject(Object response) {if (response instanceof BaseResponse) {if (((BaseResponse) response).getSuccess()) {// 如果状态是成功的,且 responseCode 未设置,则设置为 SUCCESSif (StringUtils.isEmpty(((BaseResponse) response).getResponseCode())) {((BaseResponse) response).setResponseCode(ResponseCode.SUCCESS.name());}} else {// 如果状态是失败的,且 responseCode 未设置,则设置为 BIZ_ERRORif (StringUtils.isEmpty(((BaseResponse) response).getResponseCode())) {((BaseResponse) response).setResponseCode(ResponseCode.BIZ_ERROR.name());}}}}/*** 定义并返回一个通用的失败响应。* 如果返回类型是 BaseResponse 的子类,则创建一个失败的 BaseResponse 实例并设置相关信息。** @param returnType 目标方法的返回类型。* @param throwable  抛出的异常对象。* @return 通用的失败响应对象,若返回类型不是 BaseResponse 的子类则返回 null。* @throws NoSuchMethodException     如果找不到指定的构造方法。* @throws IllegalAccessException    如果无法访问构造方法。* @throws InvocationTargetException 如果构造方法调用抛出异常。* @throws InstantiationException    如果实例化对象失败。*/private Object getFailedResponse(Class returnType, Throwable throwable)throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {// 判断返回类型是否为 BaseResponse 的子类if (returnType.getDeclaredConstructor().newInstance() instanceof BaseResponse) {// 创建 BaseResponse 实例BaseResponse response = (BaseResponse) returnType.getDeclaredConstructor().newInstance();// 设置响应状态为失败response.setSuccess(false);if (throwable instanceof BizException bizException) {// 如果是业务异常,设置响应信息和错误码response.setResponseMessage(bizException.getErrorCode().getMessage());response.setResponseCode(bizException.getErrorCode().getCode());} else if (throwable instanceof SystemException systemException) {// 如果是系统异常,设置响应信息和错误码response.setResponseMessage(systemException.getErrorCode().getMessage());response.setResponseCode(systemException.getErrorCode().getCode());} else {// 其他异常,设置响应信息和默认错误码response.setResponseMessage(throwable.toString());response.setResponseCode(ResponseCode.BIZ_ERROR.name());}return response;}// 返回类型不是 BaseResponse 的子类,记录错误日志LOGGER.error("failed to getFailedResponse , returnType (" + returnType + ") is not instanceof BaseResponse");return null;}
}

结论

那么我们在指定类上方打上 facade注解

就能实现记录日志

http://www.lqws.cn/news/161011.html

相关文章:

  • 智能标志桩图像监测装置如何守护地下电缆安全
  • html-pre标签
  • LeetCode 461.汉明距离
  • Spring MVC 之 异常处理
  • 简化复杂系统的优雅之道:深入解析 Java 外观模式
  • 数字证书_CA_详解
  • 2025年- H71-Lc179--39.组合总和(回溯,组合)--Java版
  • 二叉树的遍历总结
  • jdbc查询mysql数据库时,出现id顺序错误的情况
  • C:\Users\中文名修改为英文名
  • delphi7 链表 使用方法
  • 性能优化之SSR、SSG
  • 【前端】vue3性能优化方案
  • sourcetree取消待推送
  • 《计算机是怎么跑起来的》第二章读后感
  • 算法题(162):火烧赤壁
  • 13.4 AI颠覆语言学习:预录制视频+GPT-4评估如何实现60%成本降低与40%留存飙升
  • Seata 分布式事务 AT 模式
  • 智慧供水运维管理系统
  • LeetCode 70 爬楼梯(Java)
  • 探索未知惊喜,盲盒抽卡机小程序系统开发新启航
  • 半监督学习:低密度分离假设 (Low-Density Separation Assumption)
  • mysql密码正确SpringBoot和Datagrip却连接不上
  • c++第七天--特殊运算符的重载练习
  • day20 leetcode-hot100-38(二叉树3)
  • 第二章支线八 ·CSS终式:Tailwind与原子风暴
  • 优雅的系统重试
  • 如何轻松将视频从安卓设备传输到电脑?
  • 检测到 #include 错误。请更新 includePath。已为此翻译单元(D:\软件\vscode\test.c)禁用波形曲线
  • 【SSM】SpringMVC学习笔记8:拦截器