定义切面注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 *  切面注解
 * @Description 用于打印请求和响应日志的注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiLog {

    String[] inIgnoreValues() default {};

    String[] outIgnoreValues() default {};

    String apiName() default "";

}

方法增强处理器

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 方法增强处理器
 * @param <R> 目标方法返回值的类型
 */
public interface MethodAdviceHandler<R> {
    /**
     * 目标方法执行之前的判断,判断目标方法是否允许执行。默认返回 true,即 默认允许执行
     *
     * @param point 目标方法的连接点
     * @return 返回 true 则表示允许调用目标方法;返回 false 则表示禁止调用目标方法。
     * 当返回 false 时,此时会先调用 getOnForbid 方法获得被禁止执行时的返回值,然后
     * 调用 onComplete 方法结束切面
     */
    default boolean onBefore(ProceedingJoinPoint point) {
        return true;
    }

    /**
     * 禁止调用目标方法时(即 onBefore 返回 false),执行该方法获得返回值,默认返回 null
     *
     * @param point 目标方法的连接点
     * @return 禁止调用目标方法时的返回值
     */
    default R getOnForbid(ProceedingJoinPoint point) {
        return null;
    }

    /**
     * 目标方法抛出异常时,执行的动作
     *
     * @param point 目标方法的连接点
     * @param e     抛出的异常
     * @throws Throwable
     */
    void onThrow(ProceedingJoinPoint point, Throwable e) throws Throwable;

    /**
     * 获得抛出异常时的返回值,默认返回 null
     *
     * @param point 目标方法的连接点
     * @param e     抛出的异常
     * @return 抛出异常时的返回值
     */
    default R getOnThrow(ProceedingJoinPoint point, Throwable e) {
        return null;
    }

    /**
     * 目标方法完成时,执行的动作
     *
     * @param point     目标方法的连接点
     * @param startTime 执行的开始时间
     * @param permitted 目标方法是否被允许执行
     * @param thrown    目标方法执行时是否抛出异常
     * @param result    执行获得的结果
     */
    default void onComplete(ProceedingJoinPoint point, long startTime, boolean permitted, boolean thrown, Object result) {
    }
}
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;

/**
 * 方法增强器基础类
 * @param <R>
 */
@Slf4j
public abstract class BaseMethodAdviceHandler<R> implements MethodAdviceHandler<R> {

    /**
     * 抛出异常时候的默认处理
     */
    @Override
    public void onThrow(ProceedingJoinPoint point, Throwable e) throws Throwable {
        String methodDesc = getMethodDesc(point);
        Object[] args = point.getArgs();
        log.error("{} 执行时出错,入参={}", methodDesc, args, e);
    }

    /**
     * 获得被代理的方法
     *
     * @param point 连接点
     * @return 代理的方法
     */
    protected Method getTargetMethod(ProceedingJoinPoint point) {
        // 获得方法签名
        Signature signature = point.getSignature();
        // Spring AOP 只有方法连接点,所以 Signature 一定是 MethodSignature
        return ((MethodSignature) signature).getMethod();
    }

    /**
     * 获得方法描述,目标类名.方法名
     *
     * @param point 连接点
     * @return 目标类名.执行方法名
     */
    protected String getMethodDesc(ProceedingJoinPoint point) {
        // 获得被代理的类
        Object target = point.getTarget();
        String className = target.getClass().getSimpleName();
        Signature signature = point.getSignature();
        String methodName = signature.getName();
        return className + "." + methodName;
    }

}
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.UUID;

/**
 * 日志方法增强处理器的实现
 *
 */
@Slf4j
@Component
public class ApiLogAdviceHandler extends BaseMethodAdviceHandler<Object> {

    /**
     * 会话ID
     */
    private final static String SESSION_KEY = "sessionId";

    /**
     * 在进入controller之前拦截并打印请求报文日志
     *
     * @param point 目标方法的连接点
     * @return
     */
    @Override
    public boolean onBefore(ProceedingJoinPoint point) {
        String token = UUID.randomUUID().toString().replace("-", "");
        MDC.put(SESSION_KEY, token);

        Object[] args = point.getArgs();
        Method method = getTargetMethod(point);
        ApiLog apiLog = method.getAnnotation(ApiLog.class);
        String[] ignoreValues = apiLog.inIgnoreValues();
        SimplePropertyPreFilter propertyPreFilter = new SimplePropertyPreFilter();
        if (ignoreValues.length > 0) {
            Arrays.stream(ignoreValues).forEach(item -> propertyPreFilter.getExcludes().add(item));
        }

        ServletRequestAttributes requestAttributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (Objects.isNull(requestAttributes)) {
            log.info(
                    "==> 非http请求:" + "==> 请求报文:" + (args.length > 0 ? JSON.toJSONString(args[0], propertyPreFilter) : ""));
            return true;
        }

        HttpServletRequest request = requestAttributes.getRequest();
        String ip = HttpUtils.getIpAddress(request);
        log.info("==> 请求者IP:" + ip + "\n" + "==> 请求接口:" + request.getMethod() + " " + request.getRequestURL() + "\n"
                + "==> 请求报文:" + (args.length > 0 ? JSON.toJSONString(args[0], propertyPreFilter) : ""));
        return true;
    }

    @Override
    public void onThrow(ProceedingJoinPoint point, Throwable e) throws Throwable {
        throw e;
    }

    /**
     * 返回信息后,打印响应报文的日志
     *
     * @param point     目标方法的连接点
     * @param startTime 执行的开始时间
     * @param permitted 目标方法是否被允许执行
     * @param thrown    目标方法执行时是否抛出异常
     * @param result    执行获得的结果
     */
    @Override
    public void onComplete(ProceedingJoinPoint point, long startTime, boolean permitted, boolean thrown,
                           Object result) {
        long costTime = System.currentTimeMillis() - startTime;
        Method method = getTargetMethod(point);
        ApiLog apiLog = method.getAnnotation(ApiLog.class);
        String[] ignoreValues = apiLog.outIgnoreValues();
        SimplePropertyPreFilter propertyPreFilter = new SimplePropertyPreFilter();
        if (ignoreValues.length > 0) {
            Arrays.stream(ignoreValues).forEach(item -> propertyPreFilter.getExcludes().add(item));
        }
        log.info("<== 响应报文:{}, 耗时:{}ms", JSON.toJSONString(result, propertyPreFilter), costTime);
        MDC.remove(SESSION_KEY);
    }

}

切面类

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * 切面父类
 */
public abstract class BaseMethodAspect implements ApplicationContextAware {

    /**
     * 切点,通过 @Pointcut 指定相关的注解
     */
    protected abstract void pointcut();

    /**
     * 对目标方法进行环绕增强处理,子类需通过 pointcut() 方法指定切点
     *
     * @param point 连接点
     * @return 方法执行返回值
     */
    @Around("pointcut()")
    public Object advice(ProceedingJoinPoint point) throws Throwable {
        // 获得切面绑定的方法增强处理器的类型
        Class<? extends MethodAdviceHandler<?>> handlerType = getAdviceHandlerType();
        // 从 Spring 上下文中获得方法增强处理器的实现 Bean
        MethodAdviceHandler<?> adviceHandler = appContext.getBean(handlerType);
        // 使用方法增强处理器对目标方法进行增强处理
        return advice(point, adviceHandler);
    }

    /**
     * 获得切面绑定的方法增强处理器的类型
     *
     * @return
     */
    protected abstract Class<? extends MethodAdviceHandler<?>> getAdviceHandlerType();

    /**
     * 使用方法增强处理器增强被注解的方法
     *
     * @param point   连接点
     * @param handler 切面处理器
     * @return 方法执行返回值
     */
    private Object advice(ProceedingJoinPoint point, MethodAdviceHandler<?> handler) throws Throwable {
        // 执行之前,返回是否被允许执行
        boolean permitted = handler.onBefore(point);
        // 方法返回值
        Object result;
        // 是否抛出了异常
        boolean thrown = false;
        // 开始执行的时间
        long startTime = System.currentTimeMillis();

        // 目标方法被允许执行
        if (permitted) {
            try {
                // 执行目标方法
                result = point.proceed();
            } catch (Throwable e) {
                // 抛出异常
                thrown = true;
                // 处理异常
                handler.onThrow(point, e);
                // 抛出异常时的返回值
                result = handler.getOnThrow(point, e);
            }
        } else {
            // 目标方法被禁止执行,禁止执行时的返回值
            result = handler.getOnForbid(point);
        }
        // 结束
        handler.onComplete(point, startTime, permitted, thrown, result);
        return result;
    }

    private ApplicationContext appContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        appContext = applicationContext;
    }
}
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 *
 * 日志切面类
 */
@Aspect
@Component
@Slf4j
@Order(1)
public class ApiLogAspect extends BaseMethodAspect {

    /**
     * 定义空方法用于切点表达式
     */
    @Override
    @Pointcut("@annotation(com.baizhiedu.aop.ApiLog)")
    public void pointcut() {
    }


    @Override
    protected Class<? extends MethodAdviceHandler<?>> getAdviceHandlerType() {
        return ApiLogAdviceHandler.class;
    }
}

HttpUtils

import javax.servlet.http.HttpServletRequest;

public class HttpUtils {

    private static final String UNKNOWN = "unknown";

    /**
     * 获取真实ip地址,避免获取代理ip
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

}