SpringBoot+AOP实现日志功能(自定义注解的使用)
本文最后更新于2022.09.22-07:36
,某些文章具有时效性,若有错误或已失效,请在下方留言或联系涛哥。
什么是AOP?
AOP(Aspect-Oriented Programming:面向切面编程):是指将那些与业务无关,却被多个业务模块所共同调用逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,提升系统的可维护性。
AOP的作用
AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。
主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。
简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。
AOP的应用场景
比如典型的AOP的应用场景:
-
日志记录
-
事务管理
-
权限验证
-
性能监测
AOP可以拦截指定的方法,并且对方法增强,比如:事务、日志、权限、性能监测等增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离。
步骤
详细配置请看
1,导入maven配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.66</version>
</dependency>
2,配置一个自定义注解
package com.example.aop.config;
import java.lang.annotation.*;
/**
* @Program: aop
* @ClassName SysAnnotation
* @Description: 自定义系统操作注解
* @Author: liutao
* @DateTime: 2022/6/5 2:32
* @Version: 1.0
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysAnnotation {
String operMoudle() default "";//操作模块
String operMethod() default "";//操作方法
String operDes() default "";//操作描述
}
3,创建一个测试接口 TestController
package com.example.aop.controller;
import com.example.aop.config.SysAnnotation;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @Program: aop
* @ClassName TestController
* @Description: aop实现日志测试接口
* @Author: liutao
* @DateTime: 2022/6/5 2:29
* @Version: 1.0
*/
@RestController
public class TestController {
@RequestMapping("test")
@SysAnnotation(operMoudle = "测试接口",operMethod="test", operDes = "测试")
public String test(String param){
return param;
}
}
4,定义切面
package com.example.aop.log;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.aop.config.SysAnnotation;
import org.aspectj.lang.JoinPoint;
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.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
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.util.Arrays;
import java.util.List;
/**
* @Program: aop
* @ClassName SysLogger
* @Description: aop日志具体实现
* @Author: liutao
* @DateTime: 2022/6/5 2:38
* @Version: 1.0
*/
@Aspect
@Component
public class SysLogger {
private final Logger logger = LoggerFactory.getLogger(SysLogger.class);
@Pointcut("@annotation(com.example.aop.config.SysAnnotation)")
public void operLoggerPointCut(){
logger.info("===============系统操作日志===============");
}
@Pointcut("execution(public * com.example.aop.controller.*.*(..))")
public void controllerMethod(){}
@Before("operLoggerPointCut()")
public void logBefore(JoinPoint joinPoint){
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
StringBuilder requestLog = new StringBuilder();
Signature signature = joinPoint.getSignature();
Method method = ((MethodSignature) signature).getMethod();
SysAnnotation sysAnnotation = method.getAnnotation(SysAnnotation.class);//利用反射获取到自定义注解
// 打印请求内容
logger.info("===============请求内容开始===============");
logger.info("操作模块:"+sysAnnotation.operMoudle());
logger.info("操作方法:"+sysAnnotation.operMethod());
logger.info("操作描述:"+sysAnnotation.operDes());
logger.info("请求地址:" + request.getRequestURL().toString());
logger.info("请求IP:" + request.getRemoteAddr());
logger.info("请求方式:" + request.getMethod());
logger.info("请求类方法:" + joinPoint.getSignature());
logger.info("请求类方法参数值:" + Arrays.toString(joinPoint.getArgs()));
// 处理请求参数
String[] paramNames = ((MethodSignature) signature).getParameterNames();
Object[] paramValues = joinPoint.getArgs();
int paramLength = null == paramNames ? 0 : paramNames.length;
if (paramLength == 0) {
requestLog.append("请求参数 = {} ");
} else {
requestLog.append("请求参数 = [");
for (int i = 0; i < paramLength - 1; i++) {
requestLog.append(paramNames[i]).append("=").append(JSONObject.toJSONString(paramValues[i])).append(",");
}
requestLog.append(paramNames[paramLength - 1]).append("=").append(JSONObject.toJSONString(paramValues[paramLength - 1])).append("]");
}
logger.info("请求参数明细:"+requestLog.toString());
logger.info("===============请求内容结束===============");
}
@AfterReturning(returning = "o", pointcut = "operLoggerPointCut()")
public void logResultVOInfo(Object o){
logger.info("--------------返回内容开始----------------");
logger.info("Response内容:" + JSON.toJSONString(o));
logger.info("--------------返回内容结束----------------");
}
@AfterThrowing(pointcut = "controllerMethod()", throwing = "ex")
public void doAfterThrowing(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("连接点方法为:" + methodName + ",参数为:" + args + ",异常为:" + ex);
}
}
效果图
使用主流日志框架
市场上存在非常多的日志框架。 JUL(java.util.logging),JCL(ApacheCommons Logging),Log4j,Log4j2,Logback、 SLF4j、 jboss-logging等。
Spring Boot在框架内容部使用JCL,spring-boot-starter-logging采用了slf4j+logback的形式,Spring Boot也能自动适配(jul、 log4j2、 logback) 并简化配置。
阅读剩余
版权声明:
作者:涛哥
链接:https://ltbk.net/back/spring_family/spring-boot/article/876.html
文章版权归作者所有,未经允许请勿转载。
作者:涛哥
链接:https://ltbk.net/back/spring_family/spring-boot/article/876.html
文章版权归作者所有,未经允许请勿转载。
THE END