博客
关于我
SpringBoot应用中使用AOP 记录日志
阅读量:181 次
发布时间:2019-02-28

本文共 6366 字,大约阅读时间需要 21 分钟。

Spring Boot应用中使用AOP记录接口访问日志

AOP的应用场景

在本文中,我们将探讨如何在Spring Boot项目中使用AOP(面向切面编程)技术来实现接口访问的统一日志记录。通过在Controller层定义一个切面,我们可以实现对所有接口方法的调用进行日志记录,包括操作描述、时间、耗时、URL、请求参数和返回结果等信息。

AOP的基础知识

AOP是一种面向切面编程的技术,通过预编译和运行期动态代理实现程序功能的统一维护。AOP的主要优势在于能够对业务逻辑的各个部分进行隔离,从而降低耦合度,提高程序的可重用性和开发效率。在AOP中,通知(Advice)定义了切面要完成的工作,而切点(Pointcut)定义了通知应用的范围。

AOP的通知类型

  • 前置通知(Before)

    在目标方法调用前执行通知功能。

  • 后置通知(After)

    在目标方法返回或抛出异常后执行通知功能。

  • 返回通知(AfterReturning)

    在目标方法成功返回后执行通知功能。

  • 异常通知(AfterThrowing)

    在目标方法抛出异常后执行通知功能。

  • 环绕通知(Around)

    将目标方法包裹在自定义逻辑中执行,适用于需要控制方法执行流程的场景。

  • 切点定义

    切点定义了通知功能应用的范围。例如,execution(public * com.macro.mall.tiny.controller.*.*(..)) 表示所有Controller层的public方法都会应用该切面的通知功能。

    日志封装类

    为了统一记录接口访问日志,我们定义了一个WebLog类,用于封装日志信息:

    package com.macro.mall.tiny.dto;public class WebLog {    private String description;    private String username;    private Long startTime;    private Integer spendTime;    private String basePath;    private String uri;    private String url;    private String method;    private String ip;    private Object parameter;    private Object result;}

    AOP日志切面实现

    我们定义了一个WebLogAspect切面类,使用@Around类型的通知来实现对Controller层接口方法的环绕日志记录:

    package com.macro.mall.tiny.component;import cn.hutool.core.util.StrUtil;import cn.hutool.core.util.URLUtil;import cn.hutool.json.JSONUtil;import com.macro.mall.tiny.dto.WebLog;import io.swagger.annotations.ApiOperation;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.*;import org.aspectj.lang.reflect.MethodSignature;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestParam;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.lang.reflect.Parameter;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/** * 统一日志处理切面 */@Component@Order(1)public class WebLogAspect {    private static final Logger LOGGER = LoggerFactory.getLogger(WebLogAspect.class);    @Pointcut("execution(public * com.macro.mall.tiny.controller.*.*(..))")    public void webLog() {}    @Before("webLog()")    public void doBefore(JoinPoint joinPoint) throws Throwable {        // 初始化日志对象        WebLog webLog = new WebLog();    }    @AfterReturning(value = "webLog()", returning = "ret")    public void doAfterReturning(Object ret) throws Throwable {        // 处理返回结果        LOGGER.info("{}", JSONUtil.parse(ret));    }    @Around("webLog()")    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {        long startTime = System.currentTimeMillis();        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();        HttpServletRequest request = attributes.getRequest();        WebLog webLog = new WebLog();        Object result = joinPoint.proceed();        Signature signature = joinPoint.getSignature();        MethodSignature methodSignature = (MethodSignature) signature;        Method method = methodSignature.getMethod();        if (method.isAnnotationPresent(ApiOperation.class)) {            ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);            webLog.setDescription(apiOperation.value());        }        long endTime = System.currentTimeMillis();        webLog.setSpendTime((int) (endTime - startTime));        webLog.setStartTime(startTime);        webLog.setBasePath(StrUtil.removeSuffix(request.getRequestURL().toString(), URLUtil.url(request.getRequestURL().toString()).getPath()));        webLog.setUri(request.getRequestURI());        webLog.setUrl(request.getRequestURL().toString());        webLog.setMethod(request.getMethod());        webLog.setIp(request.getRemoteUser());        // 获取请求参数        webLog.setParameter(getParameter(method, joinPoint.getArgs()));        webLog.setResult(result);        LOGGER.info("{}", JSONUtil.parse(webLog));        return result;    }    private Object getParameter(Method method, Object[] args) {        List argList = new ArrayList<>();        Parameter[] parameters = method.getParameters();        for (int i = 0; i < parameters.length; i++) {            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);            if (requestBody != null) {                argList.add(args[i]);            }            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);            if (requestParam != null) {                Map
    map = new HashMap<>(); String key = parameters[i].getName(); if (!StringUtils.isEmpty(requestParam.value())) { key = requestParam.value(); } map.put(key, args[i]); argList.add(map); } } if (argList.size() == 0) { return null; } else if (argList.size() == 1) { return argList.get(0); } else { return argList; } }}

    接口测试

    运行项目后,访问接口可以在控制台看到日志记录:

    {    "result": {        "code": 200,        "data": {            "total": 11,            "totalPage": 11,            "pageSize": 1,            "list": [                {                    "productCommentCount": 100,                    "name": "万和",                    "bigPic": "",                    "logo": "http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180607/timg(5).jpg",                    "showStatus": 1,                    "id": 1,                    "sort": 0,                    "productCount": 100,                    "firstLetter": "W",                    "factoryStatus": 1                }            ],            "pageNum": 1        },        "message": "操作成功"    },    "basePath": "http://localhost:8080",    "method": "GET",    "parameter": [        {            "pageNum": 1        },        {            "pageSize": 1        }    ],    "description": "分页查询品牌列表",    "startTime": 1561273191861,    "uri": "/brand/list",    "url": "http://localhost:8080/brand/list",    "spendTime": 101}

    项目源码

    如需了解完整项目源码,可以访问以下地址:项目源码

    转载地址:http://dzln.baihongyu.com/

    你可能感兴趣的文章
    Python-爬虫工程师-面试总结
    查看>>
    Python-类与文件读取结合
    查看>>
    python-类思想-实现简单增删查改
    查看>>
    python-网络编程
    查看>>
    python写入文件出现空行
    查看>>
    PYTHON-返回给定x的多边形路径的y坐标
    查看>>
    python-进程池与线程池,协程
    查看>>
    Python-通过函数传递变量
    查看>>
    Python内置函数详解——总结篇
    查看>>
    Python.对象集合中的标识.和散列
    查看>>
    Python.硒.拖放错误“AttributeError:move_to 需要 WebElement“
    查看>>
    Python内置函数详解
    查看>>
    Python/Kivy:从一个类调用函数到另一个类并在 Python 中显示小部件
    查看>>
    Python/Kivy:我可以在双击标签时调用函数吗
    查看>>
    python内置函数5-frozenset()
    查看>>
    Python/Plotly:如何使用要显示的信息自定义悬停模板?
    查看>>
    Python/Selenium - 无法单击 www.instagram.com 上的“接受 cookie“按钮
    查看>>
    Python内置函数(35)——next
    查看>>
    python写入mysql
    查看>>
    python写入csv老是乱码或者各种不好
    查看>>