博客
关于我
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/

    你可能感兴趣的文章
    Objective-C实现knight Tour骑士之旅算法(附完整源码)
    查看>>
    Objective-C实现KnightTour骑士巡回赛问题算法(附完整源码)
    查看>>
    Objective-C实现KNN算法(附完整源码)
    查看>>
    Objective-C实现KNN算法(附完整源码)
    查看>>
    Objective-C实现KNN算法(附完整源码)
    查看>>
    Objective-C实现knuth morris pratt(KMP)算法(附完整源码)
    查看>>
    Objective-C实现knuth-morris-pratt(KMP)算法(附完整源码)
    查看>>
    Objective-C实现Koch snowflake科赫雪花曲线算法(附完整源码)
    查看>>
    Objective-C实现koch snowflake科赫雪花算法(附完整源码)
    查看>>
    Objective-C实现KPCA(附完整源码)
    查看>>
    Objective-C实现KruskalMST最小生成树的算法(附完整源码)
    查看>>
    Objective-C实现kruskal克鲁斯卡尔算法(附完整源码)
    查看>>
    Objective-C实现kth order statistick阶统计量算法(附完整源码)
    查看>>
    Objective-C实现lamberts ellipsoidal distance朗伯椭球距离算法(附完整源码)
    查看>>
    Objective-C实现largest AdjacentNumber最大相邻数算法 (附完整源码)
    查看>>
    Objective-C实现largest subarray sum最大子数组和算法(附完整源码)
    查看>>
    Objective-C实现largestPrime最大素数的算法 (附完整源码)
    查看>>
    Objective-C实现lazy segment tree惰性段树算法(附完整源码)
    查看>>
    Objective-C实现LBP特征提取(附完整源码)
    查看>>
    Objective-C实现LDPC码(附完整源码)
    查看>>