博客
关于我
使用AOP给springboot项目添加日志
阅读量:572 次
发布时间:2019-03-11

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

AOP(面向切面编程)作为面向对象编程的一种补充和完善,解决了后面日志追踪等横切关注点难以管理的痛点。传统的面向对象编程虽然能有效处理纵向关注点,但横向关注点的分布式特征常导致代码重复和维护难 度大,例如日志记录。

AOP的优势解析

AOP通过将系统划分为核心关注点和横切关注点,解决了传统编程的局限性。横切关注点的典型表现为高频且交错的业务流程,如权限认证、日志记录、事务管理和分布式追踪等。在Spring Boot项目中,AOP可以通过 AspectJ 框架轻松实现。

使用Spring Boot配置日志切面

第一步:加入AOP依赖

首先,也许已经掌握了Spring Boot的基础知识,但为了使用Spring AOP模块,需要在项目依赖中添加相应的坐标。我们需要添加Spring Boot Starter AOP依赖。

org.springframework.boot
spring-boot-starter-aop

第二步:创建日志切面类

创建一个全局通知器类,使用目标方法拦截所有需要处理的公共方法。正常情况下,我们可以在关注点处加上注解定义切面。

import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.component;@/component@publicclass WebLogAspect {    private final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);    // 其他自动注入的依赖    @Pointcut("execution(public * com.yt.controller.*.*(..))") // 拦截所有公共方法    public void webLog() {}    // 具体实现方法...}

关键切面注解

在实现具体逻辑之前,需要正确使用Spring的注意注解。包括定义切面方法并在切面方法上添加拦截器注解。

import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotationAfterReturning;import org.aspectj.lang.annotationAfterThrowing;import org.aspectj.lang.JoinPoint;@ component@publicclass WebLogAspect {    // 其他自动注入...    @Pointcut("execution(public * com.yt.controller.*.*(..))")    public void webLog() {}    @Before("webLog()")    public void doBefore(JoinPoint joinPoint) {        // 获取请求信息并记录日志    }    // 其他处理逻辑...}

实际应用场景

在具体实现中,对于模块信息的获取,可以像这样设计:

1.-days有多种响应状态(比如成功或失败)2. 检查返回值以确定是否存储日志3. 异常处理情况也要录入日志

###isodes

以下是完整的实现代码示例:

import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.component;@ component@publicclass WebLogAspect {    private final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);    @Autowired    private LogService logService;    @Autowired    private UserService userService;        private String url;    private String ip;    private Integer port;    private User user;    @Pointcut("execution(public * com.yt.controller..*.*(..))")    public void webLog() {}    @Before("webLog()")    public void doBefore(JoinPoint joinPoint) {        ServletRequestAttributes attributes = RequestContextHolder.getRequestAttributes();        HttpServletRequest request = attributes.getRequest();        // chỉ 登录非GET请求添加日志        if (!request.getMethod().equals("GET")) {            logger.info("请求URL: " + request.getRequestURL().toString());            url = request.getRequestURI();            ip = request.getRemoteHost();            port = request.getRemotePort();            user = UserService.getCurrentUser();        }    }    @AfterReturning(returning = "ret", pointcut = "webLog()")    public void doAfterReturning(Object ret) throws Throwable {        Integer logType = LogType.OPERATION.getId();        if (ret instanceof ResultInfo) {            ResultInfo result = (ResultInfo) ret;            String msg = result.getMsg();            String module = result.getModule();            Integer code = result.getCode();            if (!StringUtils.isBlank(msg)) {                logger.info(msg);                // 当返回码为200时记录日志                if (code != null && code.equals(200)) {                    logService.save( user, ip, port, msg, logType, module, url);                }            }        }    }    @AfterThrowing(pointcut = "webLog()", throwing = "e")    public void handleException(JoinPoint joinPoint, Throwable e) {        if (e != null) {            StringBuilder message = new StringBuilder( "程序发生错误:" );            message.append( e.getMessage() );            Logger logger = LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringClass());            logger.error( message.toString(), e );        }    }}

模块信息记录方法

在日志中增加模块信息的关键在于在返回对象中增加模块字段。这种方法确实可行,只要在目标方法返回前后进行处理即可。当然,具体实现可能需要修改目标方法的返回类型,或者在controller层增加相应的封装对象。

示例切面表达式

切面表达式的标准写法如下:

  • execution(): 表达式主体
  • public *:标识方法的访问级别和返回类型。*号表示所有类型
  • 表现形式的包命名为: comıştır表示我们要求拦截com包下面的所有类及其子类中的方法
  • 类名:*号表示所有类
  • 方法名:*号代表方法名称,括号里是参数
  • ..:表示任意数目参数可以接受
  • 因此,切面表达式"execution(public * com.yt.controller...(..))" 会拦截com.yt.controller包下的所有类中任意方法请求。

    模块化日志记录

    在实际应用中,可以通过增加模块信息的方式让日志更具可读性。例如,可以在返回结果对象中添加module字段。这有助于确定请求来源于哪个模块。以下是实现方法:

    • 在要创建的ResultInfo对象中添加module字段
    • 在目标方法的返回之前或之后处理该对象,填充模块信息
    • 在日志记录中使用该字段

    总结

    通过以上方法,可以简单而有效地为Spring Boot项目添加全局日志能力。这不仅简化了日志管理,还提高了日志的可读性和可维护性。当然,随着项目复杂性的增加,可能会出现需要更高级AOP功能的需求,如基于动态命名切面的重构或使用更复杂的数据切面。

    这之中的具体参数取值和实现细节,建议根据实际项目需求进行调整和优化。同时,结合精细化的日志配置工具,可以进一步提升日志链的能力和效果。

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

    你可能感兴趣的文章
    CoreCLR源码探索(八) JIT的工作原理(详解篇)
    查看>>
    IOS开发Swift笔记16-错误处理
    查看>>
    flume使用中的一些常见错误解决办法 (地址已经使用)
    查看>>
    andriod 开发错误记录
    查看>>
    C语言编译错误列表
    查看>>
    看明白这两种情况,才敢说自己懂跨链! | 喵懂区块链24期
    查看>>
    张一鸣:创业7年,我经历的5件事
    查看>>
    SQL基础语法
    查看>>
    git拉取远程指定分支代码
    查看>>
    CentOS5 Linux编译PHP 报 mysql configure failed 错误解决办法
    查看>>
    《web安全入门》(四)前端开发基础Javascript
    查看>>
    pycharm新建文件夹时新建python package和新建directory有什么区别?
    查看>>
    python中列表 元组 字典 集合的区别
    查看>>
    python struct 官方文档
    查看>>
    Android DEX加固方案与原理
    查看>>
    Android Retrofit2.0 上传单张图片和多张图片
    查看>>
    iOS_Runtime3_动态添加方法
    查看>>
    Leetcode第557题---翻转字符串中的单词
    查看>>
    Problem G. The Stones Game【取石子博弈 & 思维】
    查看>>
    Unable to execute dex: Multiple dex files
    查看>>