最近搜索

旅游 aop代码 备份 这个代码 不用。 检测 接口代码 君程

浏览:27
管理员 2026-03-06 09:44



package java456.com.config;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * AOP切面:记录Controller层请求的URL、IP、参数、返回值等信息
 */
@Aspect
@Component
public class RequestAspect {

    // 对接logback的日志对象(建议用INFO级别,输出到你配置的info日志文件)
    private static final Logger logger = LoggerFactory.getLogger(RequestAspect.class);

    /**
     *   execution(public * java456.com.controller.xcx_api.API_XCX_Order_Controller.*(..))
     *   public  匹配公有方法(如果去掉 public,会匹配所有访问修饰符的方法:私有 / 保护 / 公有)
     *   第一个 * 匹配任意返回值类型(比如 String、Integer、void、Order 等,不管方法返回什么都匹配)
     *   java456.com.controller.xcx_api.API_XCX_Order_Controller   精准匹配这个指定类(只拦截这个类里的方法)
     *   第二个 * 匹配这个类里的任意方法名(比如 createOrder、getOrder、cancelOrder 等,不管方法叫什么都匹配)
     *    (..)     匹配方法的任意参数(0 个参数、1 个参数、多个参数,不管参数类型是什么都匹配)
     *
     *
     *  如果想 java456.com.controller包下面所有的控制器呢。
     *  @Pointcut("execution(public * java456.com.controller.**. *(..))")
     *
     */
    @Pointcut("execution(public * java456.com.controller.xcx_api.API_XCX_Order_Controller.*(..))")
    public void log() {
    }

    /**
     * 方法执行前:记录请求URL、IP、方法、参数等
     */
    @Before("log()")
    public void deBefore(JoinPoint joinPoint) {
        try {
            // 1. 获取HttpServletRequest对象(兼容异步请求,避免空指针)
            ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (sra == null) {
                logger.warn("【请求日志】无法获取HttpServletRequest(异步请求?)");
                return;
            }
            HttpServletRequest request = sra.getRequest();

            // 2. 解析核心请求信息
            String url = request.getRequestURI(); // 请求URI(如/api/user/list)
            String ip = getClientIp(request); // 真实客户端IP(修复原代码getRemoteHost的缺陷)
            String method = request.getMethod(); // 请求方法(GET/POST等)
            String classMethod = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName(); // 执行的方法名
            Object[] args = joinPoint.getArgs(); // 方法参数

            // 3. 解析方法参数为 "key=value" 格式的字符串
            String argsStr = parseArgsToKeyValue(args);

            // 4. 拼接日志(用logger输出,而非System.out,对接logback配置)
            logger.info("【请求开始】URL:{} | IP:{} | 请求方法:{} | 执行方法:{} | 方法参数:{} | 用户信息:{}",
                    url, ip, method, classMethod, args, argsStr);

        } catch (Exception e) {
            logger.error("【请求日志】记录请求信息失败", e);
        }
    }

    /**
     * 方法执行后:记录请求结束(可选)
     */
    @After("log()")
    public void doAfter(JoinPoint joinPoint) {
        try {
            ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (sra != null) {
                String url = sra.getRequest().getRequestURI();
                logger.info("【请求结束】URL:{}", url);
            }
        } catch (Exception e) {
            logger.error("【请求日志】记录请求结束信息失败", e);
        }
    }

    /**
     * 方法返回后:记录返回值
     */
    @AfterReturning(returning = "result", pointcut = "log()")
    public void doReturn(Object result) {
        try {
            ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (sra != null) {
                String url = sra.getRequest().getRequestURI();
                // 注意:如果返回值是大对象(如List<XXX>),建议只记录关键信息,避免日志过大
                logger.info("【请求返回】URL:{} | 返回值:{}", url, result);
            }
        } catch (Exception e) {
            logger.error("【请求日志】记录返回值失败", e);
        }
    }

    // ---------------------- 辅助方法 ----------------------
    /**
     * 获取客户端真实IP(修复原代码getRemoteHost()只能拿到本机IP的问题)
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr(); // 最后兜底(可能是网关IP)
        }
        // 处理多IP场景(X-Forwarded-For格式:192.168.1.1, 10.0.0.1)
        return ip != null && ip.contains(",") ? ip.split(",")[0].trim() : ip;
    }


    /**
     * 辅助方法:将方法参数解析为 "key1=value1 | key2=value2" 格式的字符串
     * 支持基本类型、自定义对象(反射获取字段)、HttpServletRequest/Response等特殊对象过滤
     */
    private String parseArgsToKeyValue(Object[] args) {
        if (args == null || args.length == 0) {
            return "无参数";
        }

        StringBuilder argsStr = new StringBuilder();
        for (Object arg : args) {
            if (arg == null) {
                argsStr.append("null | ");
                continue;
            }

            Class<?> argClass = arg.getClass();
            // 过滤掉HttpServletRequest/Response等无意义的对象
            if (arg instanceof HttpServletRequest || arg instanceof HttpServletResponse) {
                continue;
            }

            // 处理基本类型/字符串(直接拼接)
            if (argClass.isPrimitive() || arg instanceof String || arg instanceof Number || arg instanceof Boolean) {
                argsStr.append(arg).append(" | ");
            }
            // 处理自定义对象(反射获取字段和值)
            else {
                try {
                    Field[] fields = argClass.getDeclaredFields();
                    for (Field field : fields) {
                        field.setAccessible(true); // 允许访问私有字段
                        String fieldName = field.getName();
                        Object fieldValue = field.get(arg);
                        argsStr.append(fieldName).append("=").append(fieldValue).append(" | ");
                    }
                } catch (IllegalAccessException e) {
                    argsStr.append("解析对象失败:").append(argClass.getSimpleName()).append(" | ");
                }
            }
        }

        // 去掉最后一个多余的 " | "
        if (argsStr.length() > 0) {
            argsStr.delete(argsStr.length() - 3, argsStr.length());
        }
        return argsStr.toString();
    }



}


联系站长

站长微信:xiaomao0055

站长QQ:14496453