一、简要说明
如今SpringBoot技术快速发展,采用注解开发更是现在开发的热潮。博主自己就结合自己在项目中关于日志功能的实现,采用AOP来拦截前端请求,获取我们所需的信息来实现日志功能。我会从搭建项目到能自己根据需求获得自己想要的数据,一步一步,让大家明白如何实现日志管理功能的。由于博主个人 能力有限,对 文章的描述就采用大白话,如果有描述不清楚的地方,希望各位大佬指正!
二、关于AOP的介绍
一谈到aop大家可能就会想到Spring,因为Spring具有两大特征IOC与AOP,而AOP又是里面的核心。一说AOP条件反射随口而出"切面编程",说定义大家都懂,但如何利用AOP我相信大多数人还是一头雾水。不过不用担心,我相信通过我接下来的介绍,会让大家重新有一个对AOP不一样的认识。
三、项目搭建
以下内容是关于如何创建一个SpringBoot的项目的步骤,会的小伙伴,自动跳过即可。
①:
②:③:四、准备工作
1、导入依赖
<!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--aop--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!--mybatis-plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.2</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
2、创建数据库表
关于mylog表中的字段根据自己的需求设定了,我就设置几个比较常用的,后面我会介绍还可以请求到 那些数据
四、后台代码的书写
这里博主先将基础的MVC架构的代码写出来,再写关于自定义注解的方法
1、pojo层
@Data @TableName("mylog") public class Mylog { @TableId(type = IdType.AUTO) private Integer operationId; private String operationModel; private String operationDescribe; private String requestMethod; private String requestParam; private String operationUrl; }
2、DAO层
@Mapper public interface MylogDao extends BaseMapper<Mylog> { }
3、Service层
public interface MylogService { int SaveLog(Mylog mylog); }
@Service public class MylogServiceImp implements MylogService { @Resource private MylogDao mylogDao; @Override public int SaveLog(Mylog mylog) { return mylogDao.insert(mylog); } }
4、controller层
当我们写完所有代码时,在测试的时候,就是调用的controller层下的方法,看aop是否拦截我们所需的数据。
@Controller public class MyLogController { @Log(operationModel = "登录模块" , operationDescribe = "访问登录模块") @GetMapping("/login/{name}/{age}") @ResponseBody public String Login(@PathVariable("name") String name, @PathVariable("age") Integer age){ return name+"今年"+age+"岁登录了系统"; } }
5、自定义注解(重点)
从这里开始就是我们需要学习的重点部分了,首先我们像普通注解一样定义一个自己用于切面的注解,代码如下:
@Documented @Target({ElementType.METHOD})//表示放在controller层的方法上 @Retention(RetentionPolicy.RUNTIME)//表示注解在那个阶段执行 public @interface Log { //写在注解里面,可以用于存到数据库时的对方法的描述 String operationModel() default " ";//操作模块 String operationDescribe() default " ";//操作说明 }
6、定义切面(重点)
如果只有自定义的注解,没有定义切面和切点,AOP是没有起到拦截的作用,先呈上代码再做详细 解释。
@Component// 让SpringBoot来识别到 @Aspect//切面 定义了 通知与切点的关系 public class LogAspect { private Mylog mylog; @Resource private MylogService mylogService; @Pointcut("@annotation(com.ghs.test.annotation.Log)")//定义切点 表示注解加在哪里(添加全路径) public void PointCut(){ } @Before("PointCut()") public void Before(JoinPoint joinPoint){ mylog = new Mylog(); ServletRequestAttributes requests = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); HttpServletRequest request = requests.getRequest(); //通过HttpServletRequest对象request获取method、url; mylog.setOperationUrl(request.getRequestURI()); mylog.setRequestMethod(request.getMethod()); mylog.setRequestParam(Arrays.toString(joinPoint.getArgs())); //通过反射机制获得切入点处的方法 MethodSignature signature = (MethodSignature)joinPoint.getSignature(); Method method = signature.getMethod(); //获取在自定义的注解@Log()中的内容 Log annotation = method.getAnnotation(Log.class); mylog.setOperationModel(annotation.operationModel()); mylog.setOperationDescribe(annotation.operationDescribe()); } @Around("PointCut()") public Object Around(ProceedingJoinPoint point) throws Throwable { Object proceed = point.proceed(); mylogService.SaveLog(mylog); System.out.println(mylog.toString()); return proceed; } }
上述代码中,除了有注释的地方,我会强调以下几个点:
①该代码理解
ServletRequestAttributes requests = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); HttpServletRequest request = requests.getRequest();
如果你只是拿得到代码后就只是CV,那么对你个人能力的提升是大大打折扣的。现在让我来说明一下为什么可以拿到HttpServletRequest对象以及为什么要拿到它。
为什么可以拿到HttpServletRequest对象:
首先RequestContextHolder是Spring框架中的一个工具类,在该工具类中调用其静态方法法getRequestAttributes()获取当前线程中绑定的请求属性,然后将 RequestAttributes 对象转换为 ServletRequestAttributes 对象,最后,再通过servletRequestAttributes.getRequest()得到HttpServletRequest对象。
为什么要拿到HttpServletRequest对象呢:
HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息。
换句话说,当我们得到HttpServletRequest对象,我们可以从idea中看到有那些方法;
String getMethod(); String getPathInfo(); String getPathTranslated(); default PushBuilder newPushBuilder() { return null; } String getContextPath(); String getQueryString(); String getRemoteUser(); boolean isUserInRole(String var1); Principal getUserPrincipal(); String getRequestedSessionId(); String getRequestURI(); StringBuffer getRequestURL(); String getServletPath(); HttpSession getSession(boolean var1); HttpSession getSession(); String changeSessionId(); boolean isRequestedSessionIdValid(); boolean isRequestedSessionIdFromCookie(); boolean isRequestedSessionIdFromURL();
以上等等方法都是HttpServletRequest自带的,我也是从中挑了几个比较常用的,所以我在前面说,你们可以根据自己的需求来设计需要的日志表哦。而且在我们真实的项目中需要获取session的时候,也可以通过该方法获得哦。
②该代码的理解
MethodSignature signature = (MethodSignature)joinPoint.getSignature(); Method method = signature.getMethod(); Log annotation = method.getAnnotation(Log.class);
Signature是 Java 反射 API 中的一个接口,在SpringAOP中可以通过 JoinPoint 对象获得连接点的相关信息,在该对象中通过getSignature() 方法获取到 Signature 对象,但因为我们的链接点是一个方法,可以将Signature 对象转换为 MethodSignature 对象,通过MethodSignature 对象可以获取到更加详细的方法签名信息。再通过反射获取自定义的注解,这样一来就可以得到我们自定义在@Log()里面的内容了。
③几个注解的理解
@Before("PointCut()")
表示在程序执行前执行。
@After("PointCut()")
表示在程序执行之后执行
@Around("PointCut()")
表示环绕整个程序执行
五、测试结果
我们访问MyLogController层下的"localhost:8080/login/567/18"接口,看是否能访问我们所需的信息。
效果展示
总上所述,我们成功将,需要拦截的方法描述、请求方法、请求参数、访问路径等信息在控制台输出并存到日志表中。
六、总结
通过上面的描述我们就可以实现一个基本的采用SpringAOP实现的日志管理功能了,上面的代码虽小,但还是五脏俱全了,就算应付一些比较复杂的要求,明白了原理多查查相关资料也是能搞定的。如果,这篇文章对正在阅读的小伙伴来说有一定帮助的话,小伙伴不要忘记点赞多多支持博主哦。如果需要将我们的日志管理写在项目中,就记得在留言区留言,如果需要的小伙伴比较多,我后期就写一个HTML页面的,关于日志管理的模块(包含一些基本的操作的)。