SpringBoot基于Aop实现自定义日志注解(提供Gitee源码)

news/2024/11/24 2:13:02/

前言:日志在我们的日常开发当中是必定会用到的,在每个方法的上都会习惯性打上@Log注解,这样系统就会自动帮我们记录日志,整体的代码结构就会非常优雅,这边我自己搭建了一个demo去实现了一些这个项目当中必定会用的功能。

目录

一、 什么是Aop

二、Aop常用注解

三、execution表达式简介

四、代码实现

1、引入依赖

2、创建枚举类

3、创建自定义注解

4、切面类实现

5、创建Controller类

6、运行结果

五、Gitee源码


一、 什么是Aop

Aop是一种编程思想(面向切面编程),通俗的用自己的语言来讲便是在我们进入某个方法之前、做完某个方法之后,Aop还会帮我们执行一些代码块,比如记录日志和监控等等。

二、Aop常用注解

1、@Pointcut注解:统一配置切入点

@Pointcut("@annotation(com.example.aop.annotation.Log)")
public void pointCut() {}

2、@Around注解:环绕增强

@Around(value = "pointCut()")
public Object round(ProceedingJoinPoint joinPoint){Object obj = null;try {obj = joinPoint.proceed();} catch (Throwable throwable) {throwable.printStackTrace();}return obj;
}

3、@Before注解:在目标方法执行前触发

@Before(value = "pointCut()")
public void before(){}

4、@After注解:在目标方法执行完毕后执行

@After(value = "pointCut()")
public void after(){}

5、@AfterReturning:在将返回值返回时执行

@AfterReturning(value = "pointCut()", returning = "result")
public void afterReturning(Object result){}

它们之间的执行顺序如下:

1、首先进入Around

2、执行joinPoint.proceed()之前,触发Before

3、然后执行joinPoint.proceed()

4、执行joinPoint.proceed()之后,触发After

5、最后触发AfterReturning

三、execution表达式简介

举个栗子:execution(* com.example.demo.controller..*.*(..))

标识符含义
execution()表达式的主体
第一个*号表示任意返回类型
com.example.demo.controllerAop所切的服务包名
包名后面的两个点(..)当前controller包下面的所有子包
第二个*表示所有类
.*(..)表示任何方法名,括号表示参数,两个点表示任意参数类型

四、代码实现

1、引入依赖

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--aop依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--常用工具类 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.9</version></dependency><!--阿里巴巴json--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.73</version></dependency></dependencies>

2、创建枚举类

BusinessType枚举类

package com.example.aop.enumType;public enum BusinessType {/*** 其它*/OTHER,/*** 新增*/INSERT,/*** 修改*/UPDATE,/*** 删除*/DELETE,/*** 授权*/GRANT,/*** 导出*/EXPORT,/*** 导入*/IMPORT,/*** 强退*/FORCE,/*** 生成代码*/GENCODE,/*** 清空数据*/CLEAN,
}

OperatorType枚举类

package com.example.aop.enumType;public enum OperatorType {/*** 其它*/OTHER,/*** 后台用户*/MANAGE,/*** 手机端用户*/MOBILE
}

3、创建自定义注解

package com.example.aop.annotation;import com.example.aop.enumType.BusinessType;
import com.example.aop.enumType.OperatorType;import java.lang.annotation.*;@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {/*** 模块*/public String title() default "";/*** 功能*/public BusinessType businessType() default BusinessType.OTHER;/*** 操作人类别*/public OperatorType operatorType() default OperatorType.MANAGE;/*** 是否保存请求的参数*/public boolean isSaveRequestData() default true;
}

4、切面类实现

package com.example.aop.aspect;import com.alibaba.fastjson.JSON;
import com.example.aop.annotation.Log;
import com.example.aop.enumType.HttpMethod;
import com.example.aop.utils.ServletUtils;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;@Aspect
@Component
public class LogAspect {private static final Logger log = LoggerFactory.getLogger(LogAspect.class);@Autowiredprivate HttpServletRequest request;@Pointcut("@annotation(com.example.aop.annotation.Log)")public void logPointCut() {}@Before("logPointCut()")public void doBefore(JoinPoint joinPoint) {log.info("====doBefore方法进入了====");// 获取签名Signature signature = joinPoint.getSignature();// 获取切入的包名String declaringTypeName = signature.getDeclaringTypeName();// 获取即将执行的方法名String funcName = signature.getName();log.info("即将执行方法为: {},属于{}包", funcName, declaringTypeName);// 也可以用来记录一些信息,比如获取请求的 URL 和 IPServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();// 获取请求 URLString url = request.getRequestURL().toString();// 获取请求 IPString ip = request.getRemoteAddr();log.info("用户请求的url为:{},ip地址为:{}", url, ip);}/*** 处理完请求后执行** @param joinPoint 切点*/@AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {handleLog(joinPoint, null, jsonResult);}/*** 拦截异常操作** @param joinPoint 切点* @param e 异常*/@AfterThrowing(value = "logPointCut()", throwing = "e")public void doAfterThrowing(JoinPoint joinPoint, Exception e) {handleLog(joinPoint, e, null);}protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) {try {// 获得注解Log controllerLog = getAnnotationLog(joinPoint);if (controllerLog == null) {return;}String className = joinPoint.getTarget().getClass().getName();String methodName = joinPoint.getSignature().getName();log.info("className:{}",className);log.info("methodName:{}",methodName);log.info("businessType:{}", controllerLog.businessType());log.info("operatorType:{}", controllerLog.operatorType());log.info("title:{}", controllerLog.title());// 处理设置注解上的参数getControllerMethodDescription(joinPoint, controllerLog);} catch (Exception exp) {// 记录本地异常日志log.error("==前置通知异常==");log.error("异常信息:{}", exp.getMessage());exp.printStackTrace();}}/*** 获取注解中对方法的描述信息 用于Controller层注解** @param log 日志* @throws Exception*/public void getControllerMethodDescription(JoinPoint joinPoint, Log log) throws Exception {// 是否需要保存request,参数和值if (log.isSaveRequestData()) {// 获取参数的信息,传入到数据库中。setRequestValue(joinPoint);}}/*** 获取请求的参数,放到log中* @throws Exception 异常*/private void setRequestValue(JoinPoint joinPoint) throws Exception {String requestMethod = ServletUtils.getRequest().getMethod();// TODO 2021年09月26日20:03:51  获取请求参数if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {//可以获得POST JSON 格式的参数String params = argsArrayToString(joinPoint.getArgs());log.info("params -{}",params);} else {//仅支持 Get请求 用@PathVariable("id")接收的参数 例如:http://localhost:8081/oauth2/oauth2/test/get/{id}Map<?, ?> paramsMap = (Map<?, ?>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);log.info("paramsMap -{}",paramsMap);}}/*** 是否存在注解,如果存在就获取*/private Log getAnnotationLog(JoinPoint joinPoint) throws Exception {Signature signature = joinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();if (method != null) {return method.getAnnotation(Log.class);}return null;}/*** 参数拼装*/private String argsArrayToString(Object[] paramsArray) {String params = "";if (paramsArray != null && paramsArray.length > 0) {for (int i = 0; i < paramsArray.length; i++) {if (!isFilterObject(paramsArray[i])) {Object jsonObj = JSON.toJSON(paramsArray[i]);params += jsonObj.toString() + " ";}}}return params.trim();}/*** 判断是否需要过滤的对象。** @param o 对象信息。* @return 如果是需要过滤的对象,则返回true;否则返回false。*/@SuppressWarnings("rawtypes")public boolean isFilterObject(final Object o) {Class<?> clazz = o.getClass();if (clazz.isArray()) {return clazz.getComponentType().isAssignableFrom(MultipartFile.class);} else if (Collection.class.isAssignableFrom(clazz)) {Collection collection = (Collection) o;for (Iterator iter = collection.iterator(); iter.hasNext();) {return iter.next() instanceof MultipartFile;}} else if (Map.class.isAssignableFrom(clazz)) {Map map = (Map) o;for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {Map.Entry entry = (Map.Entry) iter.next();return entry.getValue() instanceof MultipartFile;}}return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse|| o instanceof BindingResult;}}

5、创建Controller类

package com.example.aop.controller;import com.example.aop.annotation.Log;
import com.example.aop.enumType.BusinessType;
import com.example.aop.enumType.OperatorType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
public class UserController {@GetMapping("/hello")@Log(title = "认证模块",businessType = BusinessType.INSERT,operatorType = OperatorType.MANAGE,isSaveRequestData = true)public String hello(){return "hello";}
}

6、运行结果

五、Gitee源码

项目中用到的一些工具类也都在里面了,因为太多博客只提供了一些关键代码

SpringBoot基于Aop实现自定义日志注解: 我自己搭建了一个demo去实现了这个功能


http://www.ppmy.cn/news/495279.html

相关文章

Linux16.04+NVIDIA显卡驱动440.10+cuda10.0+cudnn7.6.1安装过程参考文档以及问题记录

目录 Linux系统盘制作与双系统安装 NVIDIA驱动安装cuda、cudnn安装 本文主要是为了记录Linux16.04NVIDIA显卡驱动440.10cuda10.0cudnn7.6.1安装配置过程中的参考文档&#xff0c;尤其是出现问题时参考的解决办法文档。 电脑型号是Dell XPS 8930&#xff0c;搭载GeForce GTX 108…

ssd nvme sata_NVMe SSD与传统SATA SSD

ssd nvme sata 介绍 (Introduction) This article assumes that you are competent in installing new drives in your computer, as well as the installation of the Windows operating system on new drives. 本文假定您有能力在计算机中安装新驱动器&#xff0c;以及在新驱…

Win10下安装Ubuntu16.04(UEFI+GPT)

自己摸索了好两天天&#xff0c;入了不少的坑&#xff0c;翻了不少相关博客资料的牌&#xff0c;最终把双系统搞定了。为了以后自己安装其他的机器&#xff0c;或者能给同样机型配置的小伙伴一些帮助&#xff0c;记录一下自己的安装过程及安装时候要注意的事项。 整体流程&…

高傲的win7

高傲的win7 首先为win7证明&#xff1a;本文内容和本人下载的系统版本以及硬件细节有关&#xff0c;并不能代表所有的win7种族。 好久没有写博客了&#xff0c;主要是因为把很多东西都记录在笔记里边了&#xff0c;但是感觉写博客和笔记还是不太一样&#xff0c;笔记就是随时…

DELL XPS 8930-R 从优盘安装centos7服务器

用于自己安装记录&#xff0c;有问题欢迎指正。 1、下载centos7 minimal https://www.centos.org/download/ 1&#xff09;最新版本下载 2&#xff09; 其他历史版本下载 2、制作Centos安装盘&#xff08;根据下载的系统大小选择U盘&#xff09; 1&#xff09;安装UltraISO 2&…

dell win10安装linux,(DELL台式XPS 8930)Win10下安装Ubuntu16.04(UEFI+GPT)

(DELL台式XPS 8930)Win10下安装Ubuntu16.04(UEFIGPT) (DELL台式XPS 8930)Win10下安装Ubuntu16.04(UEFIGPT) 自己摸索了好两天天&#xff0c;入了不少的坑&#xff0c;翻了不少相关博客资料的牌&#xff0c;最终把双系统搞定了。为了以后自己安装其他的机器&#xff0c;或者能给…

dell xps 8930安装win10+ubuntu双系统

注&#xff1a; 适用于如下配置&#xff1a;M.2 512GB PCIe x4 SSD2TB 7200rpm硬盘 不适用如下配置&#xff1a;1TB 7200RPM HDD 含16GB英特尔 Optane内存加速 1.重启后F2&#xff0c;在Boot标签中将secure boot设置为disabled&#xff0c;boot list option设置为UEFI&…

dell-xps-8930 台式机双硬盘 双系统安装 win10+Ubuntu

win10 Ubuntu 双系统安装 https://www.dell.com/support/article/cn/zh/cndhs1/sln308010/ubuntu-win10双系统安装教程?langzh mmp 重装完今天才发现dell官方居然有教程&#xff0c;我真是。。。。。。。。。。。。。。。 补&#xff1a;试着打开了secure boot发现反而进入…