简介
注: 开发工具使用的是IDEA
在通常的项目中,通常都需要一个用于对用户的操作进行记录的模块,主要负责记录各个用户对系统做个哪些操作;例如,张三用户成功登录系统,张三修改了个人资料,张三用户退出了系统等。对于这种全局的操作的记录,在实现代码中,常用以下两种方式:
- 调用Service层代码进行记录;
- 使用AOP思想添加注解。
1. 项目整体结构
2 代码详解
2.1 配置文件及实体类
2.1.1 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cmolong</groupId>
<artifactId>behavior-logs</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>behavior-logs</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
<!--切面AOP -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.1.2 application.yml
server:
port: 9091
servlet:
context-path: /
2.1.3 Bean实体类–OperationLog
OperationLog 是一个po类,主要用来封装操作行为记录需要使用的字段。
package com.cmolong.behaviorlogs.bean;
/**
* @program: behavior-logs
* @description: 操作日志实体类
* @author:
* @create:
*/
public class OperationLog {
// Id
private String id;
// 操作
private String Operation;
// 其他属性,通常情况下需要包括操作时间,操作人等信息。
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getOperation() {
return Operation;
}
public void setOperation(String operation) {
Operation = operation;
}
@Override
public String toString() {
return "OperationLog{" +
"id='" + id + '\'' +
", Operation='" + Operation + '\'' +
'}';
}
}
2.1.4 工具类–ContextUtils
package com.cmolong.behaviorlogs.common.util;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* @program: behavior-logs
* @description: 工具类
* @author:
* @create:
*/
public class ContextUtils {
public static HttpServletRequest getRequest() {
return getRequestAttributes().getRequest();
}
public static HttpServletResponse getResponse() {
return getRequestAttributes().getResponse();
}
public static HttpSession getSession() {
return getRequest().getSession();
}
public static ServletRequestAttributes getRequestAttributes() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());
}
public static ServletContext getServletContext() {
WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();
return context == null ? null : context.getServletContext();
}
}
2.1.5 启动类–BehaviorLogsApplication
package com.cmolong.behaviorlogs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BehaviorLogsApplication {
public static void main(String[] args) {
SpringApplication.run(BehaviorLogsApplication.class, args);
}
}
2.2 在方法上使用注解
2.2.1 自定义注解
package com.cmolong.behaviorlogs.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BehaviorAnno {
String description() default "";
}
2.2.1 切面类
package com.cmolong.behaviorlogs.aspect;
import com.cmolong.behaviorlogs.annotation.BehaviorAnno;
import com.cmolong.behaviorlogs.bean.OperationLog;
import com.cmolong.behaviorlogs.common.util.ContextUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
/**
* @program: behavior-logs
* @description: 切面
* @author: chengyantao
* @create: 2021-05-14 15:12
*/
@Aspect
@Component
public class BehaviorDetailAspect {
//@annotation是匹配拥有指定注解的方法
@Pointcut("@annotation(com.cmolong.behaviorlogs.annotation.BehaviorAnno)")
public void controllerAspect() {
}
/**
* 前置通知 用于拦截Controller层记录用户的操作
*
* @param joinPoint 切点
*/
@Before("controllerAspect()")
public void doBefore(JoinPoint joinPoint) {
// 此处在实际项目中常用,故先放在这,以备后用
HttpServletRequest httpServletRequest = ContextUtils.getRequest();
try {
// 实际开发中,在此处添加操作日志,改写OperationLog实体类
OperationLog operationLog = new OperationLog();
operationLog.setId("UUID");
// 获取描述
operationLog.setOperation(getControllerMethodDescription(joinPoint));
// 将操作日志进行保存入库,此处使用打印代替
System.out.println("注解 : " + operationLog);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取注解中对方法的描述信息
* 用于Controller层注解
*
* @param joinPoint 切点
* @return 方法描述
* @throws Exception 异常
*/
private static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class<?> targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String description = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
description = method.getAnnotation(BehaviorAnno.class).description();
break;
}
}
}
return description;
}
}
2.3 service层调用
2.3.1 IBehaviorService 接口类
package com.cmolong.behaviorlogs.service;
/**
* @program: behavior-logs
* @author:
* @create:
*/
public interface IBehaviorService {
void saveBehaviorLog(String message);
}
2.3.2 BehaviorServiceImpl 类
package com.cmolong.behaviorlogs.service.impl;
import com.cmolong.behaviorlogs.bean.OperationLog;
import com.cmolong.behaviorlogs.common.util.ContextUtils;
import com.cmolong.behaviorlogs.service.IBehaviorService;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
/**
* @program: behavior-logs
* @description: 操作日志
* @author:
* @create:
*/
@Service
public class BehaviorServiceImpl implements IBehaviorService {
/**
* @param message 操作说明
*/
@Override
public void saveBehaviorLog(String message) {
HttpServletRequest httpServletRequest = ContextUtils.getRequest();
// 此处在实际项目中常用,故先放在这,以备后用
OperationLog operationLog = new OperationLog();
operationLog.setId("UUID");
// 获取描述
operationLog.setOperation(message);
// 将操作日志进行保存入库,此处使用打印代替
System.out.println("service层 : " + operationLog);
}
}
2.4 indexController类
package com.cmolong.behaviorlogs.controller;
import com.cmolong.behaviorlogs.service.IBehaviorService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.cmolong.behaviorlogs.annotation.BehaviorAnno;
import com.cmolong.behaviorlogs.bean.OperationLog;
/**
* @program: behavior-logs
* @description: 登陆首页
* @author: chengyantao
* @create: 2021-05-14 15:32
*/
@RestController
@RequestMapping("api/")
public class indexController {
@Autowired
private IBehaviorService behaviorService;
//以下两个方法实际开发中绝大多数都是分布在两个不同的controller中,此处只做展示使用。
/**
* 注解方式
* @param operationLog
* @return
*/
@GetMapping("index")
@BehaviorAnno(description = "#operationLog.Operation()")
public String index(OperationLog operationLog){
System.out.println("index..."); // 演示使用
return "index";
}
/**
* 调用service方式
* @param operationLog
* @return
*/
@GetMapping("login")
public String Login(OperationLog operationLog){
System.out.println("Login..."); // 演示使用
// do something
behaviorService.saveBehaviorLog("用户XXXX登录系统。");
return "welcome";
}
}
2.5 测试
2.5.1 启动项目
2.5.2 测试注解
浏览器(Postman)访问地址: http://localhost:9091/api/index
结果:
浏览器页面展示 index
系统console输出:
注解 : OperationLog{id=‘UUID’, Operation=’#operationLog.Operation()’}
index…
2.5.3 service 调用
浏览器(Postman)访问地址: http://localhost:9091/api/login
结果:
浏览器页面展示 welcome
系统console输出:
Login…
service层 : OperationLog{id=‘UUID’, Operation=‘用户XXXX登录系统。’}