Java教程:如何使用切面环绕方法对所有接口进行添加出入参日志保存功能

背景:

----在很多时候我们做开发时,往往只是提供一个对外接口来进行前后端调试,或第三方系统联调,并使用log进行日志打印,每当出现问题进行排查时,只需要查看服务器日志就可以定位到问题,从而解决问题,但当接口慢慢变多,公司开发部署方案越来越成熟时,分工明确,查看服务器日志却变得不那么随意,这个时候如果还和以前一样出现问题就打开服务器查看日志文件就会变得越来越困难,所以我们必须对重要日志信息进行数据库存储,出现问题直接在浏览器页面查看即可,非常方便,本章就来教大家如何使用切面的方式完成这一操作,方便快捷,使用简单,话不多说,教学开始~

第一步、首先我们需要创建一个注解接口:

/**
 * 接口日志记录注解
 *
 * @author [email protected]
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InterfaceLog {

    /**
     * 标题
     */
    public String title() default "";

}

第二步、创建接口日志保存实体类:

/**
 * 接口日志对象 interface_log
 *
 * @author [email protected]
 */
@Data
public class InterfaceLog extends BaseEntity {
    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    private String id;

    /**
     * 接口说明
     */
    private String title;

    /**
     * 接口入参
     */
    private String requestParam;

    /**
     * 响应参数
     */
    private String responseParam;

    /**
     * 方法名称
     */
    private String methodName;

    /**
     * 请求方式
     */
    private String requestWay;

    /**
     * 请求耗时(ms)
     */
    private String handleTime;

    /**
     * 请求时间 yyyy-MM-dd HH:mm:ss
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date requestTime;

    /**
     * 错误信息
     */
    private String errorInfo;

    /**
     * 错误码
     */
    private String errorCode;

    /**
     * 用户ip
     */
    private String userIp;

    /**
     * 服务器ip
     */
    private String serverIp;

    /**
     * 接口状态(0正常 1异常)
     */
    private String status;
}

第三步、创建我们的切面类,将第一步接口进行切入:

/**
 * 接口日志记录处理
 *
 * @author [email protected]
 */
@Aspect
@Component
public class InterfaceLogAspect {
    private static final Logger log = LoggerFactory.getLogger(InterfaceLogAspect.class);

    // 接口日志
    @Autowired
    private InterfaceLogService interfaceLogService;

    // 配置切入点
    @Pointcut("@annotation(com.feilin.InterfaceLog)")
    public void logPointCut() {
    }

    @Around("logPointCut()")
    public Object aroundBusAsMethod(ProceedingJoinPoint joinPoint) {
        // 获得注解
        InterfaceLog controllerLog = getAnnotationLog(joinPoint);
        if (controllerLog == null) {
            return null;
        }
        Object[] args = joinPoint.getArgs();
        long time;
        // 进入方法之前的时间
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        InterfaceLog interfaceLog = new InterfaceLog();
        // 请求的地址
        String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        interfaceLog.setUserIp(ip);
        interfaceLog.setTitle(controllerLog.title());
        String hostAddress = "";
        try {
            hostAddress = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
            log.error("获取服务器ip异常,原因={}", e.toString());
        }
        interfaceLog.setServerIp(hostAddress);

        // 设置方法名称
        String methodName = joinPoint.getSignature().getName();
        interfaceLog.setMethodName(methodName);
        Object respObj = null;
        // 设置请求方式
        interfaceLog.setRequestWay(ServletUtils.getRequest().getMethod());
        try {
            respObj = joinPoint.proceed(args);
        } catch (Throwable e) {
            log.error("获取方法响应异常,原因:{}", e.toString());
        }
        stopWatch.stop();
        time = stopWatch.getTotalTimeMillis();
        // 请求时间与耗时
        interfaceLog.setHandleTime(String.valueOf(time));
        interfaceLog.setRequestTime(new Date());
        // 请求入参出参
        interfaceLog.setRequestParam(StringUtils.substring(argsArrayToString(args), 0, 2048));
        if (!ObjectUtils.isEmpty(respObj)) {
            interfaceLog.setResponseParam(StringUtils.substring(JSON.toJSONString(respObj), 0, 2048));
            JSONObject parse = (JSONObject) JSONObject.parse(interfaceLog.getResponseParam());
            String code = parse.getString("code");
            String msg = parse.getString("msg");
            if (!"0".equals(code)) {
                interfaceLog.setErrorCode(code);
                interfaceLog.setErrorInfo(msg);
                interfaceLog.setStatus("1");
            } else {
                interfaceLog.setStatus("0");
            }
        }
        try {
            interfaceLogService.insertInterfaceLog(interfaceLog);
        } catch (Throwable e) {
            log.error("接口日志保存失败,interfaceLog={},原因={}", interfaceLog.toString(), e.toString());
        }
        return respObj;
    }

    /**
     * 是否存在注解,如果存在就获取
     */
    private InterfaceLog getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(InterfaceLog.class);
        }
        return null;
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (ArrayUtils.isNotEmpty(paramsArray) && paramsArray.length > 0 && !(paramsArray[0] instanceof BeanPropertyBindingResult)) {
            params = JSON.toJSON(paramsArray[0]).toString();
        } else {
            log.info("切面日志保存/打印,获取参数为空");
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象,则返回true;否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Iterator iter = collection.iterator(); iter.hasNext(); ) {
                return iter.next() instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); ) {
                Map.Entry entry = (Map.Entry) iter.next();
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }
}

第四步、在我们的请求接口上加上注解:

@InterfaceLog(title = "获取参数")
@PostMapping("/test")
public Map<String, String> test(@RequestBody Map<String, String> parms){
	return parms;
}

最后一步、使用PostMan进行测试,大功告成!

Java教程:如何使用切面环绕方法对所有接口进行添加出入参日志保存功能_第1张图片

本次教程到这里就结束了,希望大家多多关注支持(首席摸鱼师 微信同号),持续跟踪最新文章吧~

你可能感兴趣的:(java,开发语言)