Springboot AOP 提高代码开发效率

前文:最近和前端调试接口,总是有时候发现**参数对应不上,出现或多或少的情况,**这时候你每次打印就很麻烦,我们可以采用AOP设计方法!

这个工具类可以实现每次请求接口,获取必要参数,
比如ip地址 方法名 接口名 接口参数方便你定位问题

还有一个特别重要的就是返回参数
我们可以通过AOP 的 @AfterReturning注解实现查看接口
返回参数内容

这里友情提示:post 请求返回的是JSON格式 如果传fromDate格式,也就是
@RequeryParm

package io.sugo.cdp.upload.download.config.aop;

        import com.alibaba.fastjson.JSON;
        import org.aspectj.lang.JoinPoint;
        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.aspectj.lang.reflect.MethodSignature;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
        import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
        import org.springframework.core.annotation.Order;
        import org.springframework.stereotype.Component;
        import org.springframework.web.context.request.RequestContextHolder;
        import org.springframework.web.context.request.ServletRequestAttributes;
        import org.springframework.web.multipart.MultipartFile;

        import javax.servlet.http.HttpServletRequest;
        import javax.servlet.http.HttpServletResponse;
        import java.lang.reflect.Method;

@Aspect
@Component
public class AopLogAspect {

    private Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * execution 指定controller
     * !execution 排查指定controller下的方法
     */
    @Pointcut("(execution(* io.sugo.cdp.upload.download.controller.*.*(..)) && !execution(* io.sugo.cdp.upload.download.controller.CdpUploadController.upload(..)))")
    public void aopLogAspect() {
    }


    @Before("aopLogAspect()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        logger.info("请求开始 : ====================================================================================");
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 记录下请求内容
        logger.info("请求类型 :" + request.getMethod() + "  " + "请求URL : " + request.getRequestURL());
        logger.info("请求IP  : " + request.getRemoteAddr());
        logger.info("请求方法 : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());

        // 只记录post方法 传json格式的数据
        if ("POST".equals(request.getMethod())) {
            LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
            String[] paramNames = u.getParameterNames(method);
            //方法 1 请求的方法参数值 JSON 格式 null不显示
            if (joinPoint.getArgs().length > 0) {
                Object[] args = joinPoint.getArgs();
                for (int i = 0; i < args.length; i++) {
                    //请求参数类型判断过滤,防止JSON转换报错
                    if (args[i] instanceof HttpServletRequest || args[i] instanceof HttpServletResponse || args[i] instanceof MultipartFile) {
                        continue;
                    }
                    logger.info("请求参数名称 :" + paramNames[i] + ", 内容 :" + JSON.toJSONString(args[i]));
                }
            }
        } else {
            //请求的方法参数值 兼容fromDate格式和JSON格式
            Object[] args = joinPoint.getArgs();
            // 请求的方法参数名称 显示所有字段 值为null也显示该字段
            LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
            String[] paramNames = u.getParameterNames(method);
            if (args != null && paramNames != null) {
                String params = "";
                for (int i = 0; i < args.length; i++) {
                    //请求参数类型判断过滤,
                    if (args[i] instanceof HttpServletRequest || args[i] instanceof HttpServletResponse || args[i] instanceof MultipartFile) {
                        continue;
                    }
                    params += " " + paramNames[i] + ": " + args[i] + ",";
                }
                logger.info("请求参数 :" + params);
            }
        }
    }

    @AfterReturning(returning = "ret", pointcut = "aopLogAspect()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 处理完请求,返回内容
        logger.info("返回内容 : " + JSON.toJSONString(ret));
        logger.info("请求结束 :====================================================================================");

    }


}



<!--aop 切面 -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.3</version>
    </dependency>`

`
AOP概念

来让我们看一下上面的一个小故事和 AOP 到底有什么对应关系. 首先我们知道, 在 Spring AOP 中 join point 指代的是所有方法的执行点, 而 point cut 是一个描述信息, 它修饰的是 join point, 通过 point cut, 我们就可以确定哪些 join point 可以被织入 Advice. 对应到我们在上面举的例子, 我们可以做一个简单的类比, join point 就相当于 爪哇的小县城里的百姓 , point cut 就相当于 老王所做的指控, 即凶手是个男性, 身高约七尺五寸 , 而 advice 则是施加在符合老王所描述的嫌疑人的动作: 抓过来审问 . 为什么可以这样类比呢?

join point --> 爪哇的小县城里的百姓: 因为根据定义, join point 是所有可能被织入 advice 的候选的点, 在 Spring AOP中, 则可以认为所有方法执行点都是 join point. 而在我们上面的例子中, 命案发生在小县城中, 按理说在此县城中的所有人都有可能是嫌疑人。
point cut --> 男性, 身高约七尺五寸: 我们知道, 所有的方法(joint point) 都可以织入 advice, 但是我们并不希望在所有方法上都织入 advice, 而 pointcut 的作用就是提供一组规则来匹配joinpoint, 给满足规则的 joinpoint 添加 advice. 同理, 对于县令来说, 他再昏庸, 也知道不能把县城中的所有百姓都抓起来审问, 而是根据凶手是个男性, 身高约七尺五寸, 把符合条件的人抓起来. 在这里 凶手是个男性, 身高约七尺五寸 就是一个修饰谓语, 它限定了凶手的范围, 满足此修饰规则的百姓都是嫌疑人, 都需要抓起来审问。
advice --> 抓过来审问, advice 是一个动作, 即一段 Java 代码, 这段 Java 代码是作用于 point cut 所限定的那些 join point 上的. 同理, 对比到我们的例子中, 抓过来审问 这个动作就是对作用于那些满足 男性, 身高约七尺五寸 的爪哇的小县城里的百姓。
aspect: aspect 是 point cut 与 advice 的组合, 因此在这里我们就可以类比: “根据老王的线索, 凡是发现有身高七尺五寸的男性, 都要抓过来审问” 这一整个动作可以被认为是一个 aspect。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐