本文共 4527 字,大约阅读时间需要 15 分钟。
AOP(面向切面编程)作为面向对象编程的一种补充和完善,解决了后面日志追踪等横切关注点难以管理的痛点。传统的面向对象编程虽然能有效处理纵向关注点,但横向关注点的分布式特征常导致代码重复和维护难 度大,例如日志记录。
AOP通过将系统划分为核心关注点和横切关注点,解决了传统编程的局限性。横切关注点的典型表现为高频且交错的业务流程,如权限认证、日志记录、事务管理和分布式追踪等。在Spring Boot项目中,AOP可以通过 AspectJ 框架轻松实现。
首先,也许已经掌握了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.yt.controller...(..))" 会拦截com.yt.controller包下的所有类中任意方法请求。
在实际应用中,可以通过增加模块信息的方式让日志更具可读性。例如,可以在返回结果对象中添加module字段。这有助于确定请求来源于哪个模块。以下是实现方法:
通过以上方法,可以简单而有效地为Spring Boot项目添加全局日志能力。这不仅简化了日志管理,还提高了日志的可读性和可维护性。当然,随着项目复杂性的增加,可能会出现需要更高级AOP功能的需求,如基于动态命名切面的重构或使用更复杂的数据切面。
这之中的具体参数取值和实现细节,建议根据实际项目需求进行调整和优化。同时,结合精细化的日志配置工具,可以进一步提升日志链的能力和效果。
转载地址:http://hfhvz.baihongyu.com/