背景
在系统中对外部系统发起HTTP请求时,需要记录所有失败和异常的相关请求信息至本地,在实现阶段设计两套方式来实现,一种是利用自定义注解的方式,以切面的方式来实现。另一种是在使用mybatisPlus的时候将请求的方法,在进行一次包装(父类或者单独包装为一个bean都可以),在使用时只需要调用包装后的方法即可。
使用AOP切面的方法
定义两个自定义注解@RequestLog、@ParameterType
@RequestLog:作用于具体的HTTP方法上,标记切入点。
@ParameterType:作用于方法参数上,用户获取日志需要的入参。
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestLog {
RequestTypeEnum requestMethod() default RequestTypeEnum.GET;
}
复制代码
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestLog {
RequestTypeEnum requestMethod() default RequestTypeEnum.GET;
}
复制代码
定义请求方式以及参数类型
@AllArgsConstructor
@Getter
public enum RequestTypeEnum {
/**
*请求方式
*/
GET,
POST,
PUT,
DELETE;
}
复制代码
@AllArgsConstructor
@Getter
public enum ParameterTypeEnum {
/**
*请求参数类型
*/
URL,
DATA,
HEADER,
CONFIG;
}
复制代码
定义切点,将自定义注解作用于方法上
@RequestLog(requestMethod = RequestTypeEnum.POST)
public JSONObject httpSmartPostJson(@ParameterType(parameterType = ParameterTypeEnum.URL) String url,
@ParameterType(parameterType = ParameterTypeEnum.DATA) String paramJson,
@ParameterType(parameterType = ParameterTypeEnum.CONFIG) SmartOutboundConfig.ServiceInfo serviceInfo) {
//具体调用业务}
复制代码
定义切面
//配置切面
@Component
@Aspect
@Slf4j
@Order(1)
public class RequestLogAspect {
@Autowired
private SmartLogService smartLogService;
/**
* 配置切入点
*/
@Pointcut(value = "@annotation(com.gw.scrm.outbound.constants.annotation.RequestLog)")
public void requestAspect() {
}
//选择切入方式
@Around("requestAspect()")
public Object handle(ProceedingJoinPoint joinPoint) {
SmartLog smartLog = new SmartLog();
Object proceed = null;
try {
//获取签名以及方法、参数信息
Signature signature = joinPoint.getSignature();
//格式化为MethodSignature
MethodSignature methodSignature = (MethodSignature) signature;
//获取目标方法
Method targetMethod = methodSignature.getMethod();
//获取目标方法上的注解
RequestLog annotation = targetMethod.getAnnotation(RequestLog.class);
if (annotation != null) {
smartLog.setRequestType(annotation.requestMethod().name());
}
//获取目标方法的参数注解,1维是参数,2维是注解
Annotation[][] annotations = targetMethod.getParameterAnnotations();
//获取参数
Object[] args = joinPoint.getArgs();
for (int i = 0; i < annotations.length; i++) {
Object param = args[i];
Annotation[] paramAnn = annotations[i];
//参数为空,直接下一个参数
if (param == null || paramAnn.length == 0) {
continue;
}
for (Annotation paramAnnotation : paramAnn) {
//这里判断当前注解是否为ParameterType
if (paramAnnotation instanceof ParameterType) {
//获取参数类型
ParameterTypeEnum parameterType = ((ParameterType) paramAnnotation).parameterType();
//根据注解中的值获取需要的参数
if (parameterType == ParameterTypeEnum.URL) {
smartLog.setRequestUrl(String.valueOf(param));
} else if (parameterType == ParameterTypeEnum.DATA) {
smartLog.setRequestParameter(String.valueOf(param));
}
}
}
}
//获取返回值
proceed = joinPoint.proceed();
if (proceed != null) {
Integer returnCode = JSON.parseObject(proceed.toString()).getInteger(RETURN_CODE);
//判断是否失败,失败则保存记录至本地
if (!ResultEnum.isSuccess(returnCode)) {
//请求失败
smartLog.setStatue(1);
smartLog.setResponseResult(proceed.toString());
smartLogService.save(smartLog);
}
}
} catch (Throwable throwable) {
throwable.printStackTrace();
//异常信息也记录至本地
smartLog.setStatue(2);
smartLog.setExceptionInfo(throwable.toString());
smartLogService.save(smartLog);
}
return proceed;
}
}
复制代码
对请求方法包装的方法
通过在父类中实现mybatisPlus的Service层,并在父类中调用HTTP工具类的请求方法,并对该方法添加日志接口,各个具体实现层继承该父类。
创建父类实现mybatisPlus的Service层
@Slf4j
public class BaseService<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements IService<T> {
@Autowired
private SmartLogService smartLogService;
public JSONObject invokeWithLog(String url, SmartOutboundConfig.ServiceInfo serviceInfo) {
return invokeWithLog(url, null, serviceInfo);
}
//记录日志
public JSONObject invokeWithLog(String url, String paramJson, SmartOutboundConfig.ServiceInfo serviceInfo) {
JSONObject jsonObject = null;
SmartLog smartLog = new SmartLog();
smartLog.setRequestType("POST");
smartLog.setRequestUrl(url);
smartLog.setRequestParameter(paramJson);
try {
jsonObject = HttpUtil.httpSmartPostJson(url,paramJson, serviceInfo);
if (jsonObject != null) {
Integer returnCode = JSON.parseObject(jsonObject.toString()).getInteger(RETURN_CODE);
if (!ResultEnum.isSuccess(returnCode)) {
//请求失败
smartLog.setStatue(1);
smartLog.setResponseResult(jsonObject.toString());
smartLogService.save(smartLog);
}
}
} catch (Exception e) {
e.printStackTrace();
smartLog.setStatue(2);
smartLog.setExceptionInfo(e.toString());
smartLogService.save(smartLog);
}
return jsonObject;
}
}
复制代码
实现类继承BaseService
@Service
@Slf4j
public class SmartClueService extends BaseService<SmartClueMapper, SmartClue> {
@Autowired
SmartOutboundConfig smartOutboundConfig;
@Autowired
SmartDealerShopService smartDealerShopService;
public GenerateTelRes generateSmartPhone(GenerateSmartPhoneReq req) {
//调用父类中的invokeWithLog()方法实现日志记录
invokeWithLog( url, paramJson, config) {
}
}
复制代码