AOP把软件的功能模块分为两个部分:核心关注点和横切关注点。业务处理的主要功能为核心关注点,而非核心、需要拓展的功能为横切关注点。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点进行分离,使用切面有以下好处:

  • 集中处理某一关注点/横切逻辑

  • 可以很方便的添加/删除关注点

  • 侵入性少,增强代码可读性及可维护性 因此当想打印请求日志时很容易想到切面,对控制层代码0侵入

@Aspect => 声明该类为一个注解类

@Pointcut => 定义一个切点,可以简化代码

  • @Before => 在切点之前执行代码

  • @After => 在切点之后执行代码

  • @AfterReturning => 切点返回内容后执行代码,可以对切点的返回值进行封装

  • @AfterThrowing => 切点抛出异常后执行

  • @Around => 环绕,在切点前后执行代码

使用@Pointcut定义切点

@Pointcut("execution(* your_package.controller..*(..))")

=====>@Pointcut定义了一个切点,因为是请求日志切边,因此切点定义的是Controller包下的所有类下的方法。定义切点以后在通知注解中直接使用requestServer方法名就可以了

使用@Before再切点前执行

======>@Before("requestServer()")
public void doBefore(JoinPoint joinPoint) {
    ServletRequestAttributes attributes = (ServletRequestAttributes)
RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();

    LOGGER.info("===============================Start========================");
    LOGGER.info("IP                 : {}", request.getRemoteAddr());
    LOGGER.info("URL                : {}", request.getRequestURL().toString());
    LOGGER.info("HTTP Method        : {}", request.getMethod());
    LOGGER.info("Class Method       : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
}

使用@Around打印进入控制层的入参

=====>@Around("requestServer()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    long start = System.currentTimeMillis();
    Object result = proceedingJoinPoint.proceed();
    LOGGER.info("Request Params       : {}", getRequestParams(proceedingJoinPoint));
    LOGGER.info("Result               : {}", result);
    LOGGER.info("Time Cost            : {} ms", System.currentTimeMillis() - start);

    return result;
}

========>getRquestParams方法

private Map<String, Object> getRequestParams(ProceedingJoinPoint proceedingJoinPoint) {
     Map<String, Object> requestParams = new HashMap<>();

      //参数名
     String[] paramNames = ((MethodSignature)proceedingJoinPoint.getSignature()).getParameterNames();
     //参数值
     Object[] paramValues = proceedingJoinPoint.getArgs();

     for (int i = 0; i < paramNames.length; i++) {
         Object value = paramValues[i];

         //如果是文件对象
         if (value instanceof MultipartFile) {
             MultipartFile file = (MultipartFile) value;
             value = file.getOriginalFilename();  //获取文件名
         }

         requestParams.put(paramNames[i], value);
     }

     return requestParams;
 }

@After方法调用后执行

======>@After("requestServer()")
public void doAfter(JoinPoint joinPoint) {
    LOGGER.info("===============================End========================");
}

<==================>

最后放一下完整日志请求切面代码:

@Component
@Aspect
public class RequestLogAspect {
    private final static Logger LOGGER = LoggerFactory.getLogger(RequestLogAspect.class);

    @Pointcut("execution(* your_package.controller..*(..))")
    public void requestServer() {
    }

    @Around("requestServer()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Object result = proceedingJoinPoint.proceed();
        RequestInfo requestInfo = new RequestInfo();
                requestInfo.setIp(request.getRemoteAddr());
        requestInfo.setUrl(request.getRequestURL().toString());
        requestInfo.setHttpMethod(request.getMethod());
        requestInfo.setClassMethod(String.format("%s.%s", proceedingJoinPoint.getSignature().getDeclaringTypeName(),
                proceedingJoinPoint.getSignature().getName()));
        requestInfo.setRequestParams(getRequestParamsByProceedingJoinPoint(proceedingJoinPoint));
        requestInfo.setResult(result);
        requestInfo.setTimeCost(System.currentTimeMillis() - start);
        LOGGER.info("Request Info      : {}", JSON.toJSONString(requestInfo));

        return result;
    }

    @AfterThrowing(pointcut = "requestServer()", throwing = "e")
    public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        RequestErrorInfo requestErrorInfo = new RequestErrorInfo();
        requestErrorInfo.setIp(request.getRemoteAddr());
        requestErrorInfo.setUrl(request.getRequestURL().toString());
        requestErrorInfo.setHttpMethod(request.getMethod());
        requestErrorInfo.setClassMethod(String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(),
                joinPoint.getSignature().getName()));
        requestErrorInfo.setRequestParams(getRequestParamsByJoinPoint(joinPoint));
        requestErrorInfo.setException(e);
        LOGGER.info("Error Request Info      : {}", JSON.toJSONString(requestErrorInfo));
    }

    /**
     * 获取入参
     * @param proceedingJoinPoint
     *
     * @return
     * */
    private Map<String, Object> getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
        //参数名
        String[] paramNames = ((MethodSignature)proceedingJoinPoint.getSignature()).getParameterNames();
        //参数值
        Object[] paramValues = proceedingJoinPoint.getArgs();

        return buildRequestParam(paramNames, paramValues);
    }

    private Map<String, Object> getRequestParamsByJoinPoint(JoinPoint joinPoint) {
        //参数名
        String[] paramNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
        //参数值
        Object[] paramValues = joinPoint.getArgs();

        return buildRequestParam(paramNames, paramValues);
    }

    private Map<String, Object> buildRequestParam(String[] paramNames, Object[] paramValues) {
        Map<String, Object> requestParams = new HashMap<>();
        for (int i = 0; i < paramNames.length; i++) {
            Object value = paramValues[i];

            //如果是文件对象
            if (value instanceof MultipartFile) {
                MultipartFile file = (MultipartFile) value;
                value = file.getOriginalFilename();  //获取文件名
            }

            requestParams.put(paramNames[i], value);
        }

        return requestParams;
    }

    @Data
    public class RequestInfo {
        private String ip;
        private String url;
        private String httpMethod;
        private String classMethod;
        private Object requestParams;
        private Object result;
        private Long timeCost;
    }

    @Data
    public class RequestErrorInfo {
        private String ip;
        private String url;
        private String httpMethod;
        private String classMethod;
        private Object requestParams;
        private RuntimeException exception;
    }
}

AOP 日志切面的更多相关文章

  1. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存

    代码已上传Github+Gitee,文末有地址 上回<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之九 || 依赖注入IoC学习 + ...

  2. Spring Boot 2.X(八):Spring AOP 实现简单的日志切面

    AOP 1.什么是 AOP ? AOP 的全称为 Aspect Oriented Programming,译为面向切面编程,是通过预编译方式和运行期动态代理实现核心业务逻辑之外的横切行为的统一维护的一 ...

  3. Z从壹开始前后端分离【 .NET Core2.0/3.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存

    本文梯子 本文3.0版本文章 代码已上传Github+Gitee,文末有地址 大神反馈: 零.今天完成的深红色部分 一.AOP 之 实现日志记录(服务层) 1.定义服务接口与实现类 2.在API层中添 ...

  4. [转] AOP面向切面编程

    AOP面向切面编程 AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. ...

  5. 【原创】Android AOP面向切面编程AspectJ

    一.背景: 在项目开发中,对 App 客户端重构后,发现用于统计用户行为的友盟统计代码和用户行为日志记录代码分散在各业务模块中,比如在视频模块,要想实现对用户对监控点的实时预览和远程回放行为进行统计, ...

  6. 论AOP面向切面编程思想

    原创: eleven 原文:https://mp.weixin.qq.com/s/8klfhCkagOxlF1R0qfZsgg [前言] AOP(Aspect-Oriented Programming ...

  7. 学习笔记: AOP面向切面编程和C#多种实现

    AOP:面向切面编程   编程思想 OOP:一切皆对象,对象交互组成功能,功能叠加组成模块,模块叠加组成系统      类--砖头     系统--房子      类--细胞     系统--人    ...

  8. SpringBoot系列——aop 面向切面

    前言 项目中我们经常会用到aop切面,比如日志记录:这里简单记录一下springboot是如何使用aop spring对aop的配置,来自springboot参考手册,Common applicati ...

  9. javascript AOP(面向切面编程)

    var func = function () { console.log("2") } Function.prototype.before = function (beforefn ...

随机推荐

  1. 学习java的第十六天

    一.今日收获 1.完成了手册第二章没有验证完成的例题 2.预习了第三章的算法以及for语句与if语句的用法 二.今日难题 1.验证上出现问题,没有那么仔细. 2.第二章还有没有完全理解的问题 三.明日 ...

  2. Spark基础:(三)Spark 键值对操作

    1.pair RDD的简介 Spark为包含键值对类型的RDD提供了一些专有的操作,这些RDD就被称为pair RDD 那么如何创建pair RDD呢? 在不同的语言中有着不同的创建方式 在pytho ...

  3. 容器的分类与各种测试(二)——vector部分用法

    向量 vector 是一种对象实体, 能够容纳许多其他类型相同的元素, 因此又被称为容器. 与string相同, vector 同属于STL(Standard Template Library, 标准 ...

  4. javaAPI1

    Iterable<T>接口, Iterator<T> iterator() Collection<E>:接口,add(E e) ,size() , Object[] ...

  5. Output of C++ Program | Set 5

    Difficulty Level: Rookie Predict the output of below C++ programs. Question 1 1 #include<iostream ...

  6. react中在hooks方法useEffect中加载异步数据

    useEffect( ()=>{ (async function getPipeList(value:any) { let result= await GetPipeList(value); s ...

  7. 在html页面通过绝对地址显示图片

    1.编辑tomcat中conf目录下的server.xml文件,在<Host></Host>中添加如下代码段 <Context path="/D" d ...

  8. 12月第二周bug总结

    1.bug总结 复制 别人的依赖和依赖指定类型 报错 解决:依赖还没加载完成,你就指定了版本型号,所以报错,所以先让他加载依赖,后指定该型号 eureka(优瑞卡) 注册服务 控制台没有显示出来的话 ...

  9. Mysql解决主从慢同步问题

    目录 一.简介 为何有延迟 二.观察 三.解决办法 参数 多线程 组提交 一.简介 一般主从复制,有三个线程参与,都是单线程:Binlog Dump(主) ----->IO Thread (从) ...

  10. [BUUCTF]REVERSE——[BJDCTF 2nd]guessgame

    [BJDCTF 2nd]guessgame 附件 步骤: 例行查壳儿,64位程序,没有壳儿 64位ida载入,习惯性的检索程序里的字符串,看到了一串类似flag的字符串,拿去提交,成功 BJD{S1m ...