一、准备部分:
创建项目:
Maven导入jar包:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.phl.aop</groupId>
<artifactId>aop_test_parent_0810</artifactId>
<version>1.0-SNAPSHOT</version>
<name>aop_test_parent_0810</name>
<!--继承SpringBoot-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<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>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--mybatis-plus支持-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件:
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql:///数据库名称?characterEncoding=utf-8
username: root
password: 密码
mybatis-plus:
mapper-locations: classpath:cn/phl/aop/mapper/*Mapper.xml
MySQL数据库表准备:建表语句参考
CREATE TABLE `log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`visitTime` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
`username` varchar(255) DEFAULT NULL,
`ip` varchar(255) DEFAULT NULL,
`url` varchar(255) DEFAULT NULL,
`executionTime` bigint(20) DEFAULT NULL,
`method` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
二、编写测试相关的类
说明:本测试主要通过UserController的调用来测试,但是鉴于主要目的是测试操作日志,所以对User实体类及其Service层和dao层代码省略,故需要调用的地方使用其他代码进行替代表示。同时对日志的展示接口等代码省略。代码中省略自定义包的导入代码
1、实体类domain
Log类:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* 日志实体类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Log {
private Long id;//主键
private Date visitTime;//访问时间
private String username;//用户
private String ip;//访问IP地址
private String url;//访问url地址
private Long executionTime;//执行时长
private String method;//访问方法(保存形式:方法名-日志操作方法描述)
}
2.service层:接口和实现类
接口:ILogService
/**
* Log实体对象的Service接口
*/
public interface ILogService {
int save(Log log);//保存Log对象
}
实现类:LogServiceImpl
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class LogServiceImpl implements ILogService {
@Autowired
private LogMapper logMapper;
@Override
public int save(Log log) {
return logMapper.save(log);
}
}
3.mapper:
mapper接口:
public interface LogMapper {
int save(Log log);
}
LogMapper.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.phl.aop.mapper.LogMapper">
<insert id="save" useGeneratedKeys="true" keyProperty="id">
INSERT INTO log(visitTime,username,ip,url,executionTime,method)
VALUE (#{visitTime},#{username},#{ip},#{url},#{executionTime},#{method})
</insert>
</mapper>
4.自定义注解
LogDescribe :在方法上使用,@LogDescribe (describe=“描述内容”),在切面中进行获取
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogDescribe {
String describe();//对日志操作的描述
}
5.AOP切面:
LogAOP:通知类(包含前置通知和后置通知—指定切面),后置通知中有获取自定义注解属性值的操作
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
/**
* 日志的通知类:前置通知和后置通知是对所有的controller层对象进行记录()
*/
@Component
@Aspect
public class LogAop {
private Date visitTime;
private Class clazz;
private Method method;
private String url;
private String ip;
/**
* 前置通知:
* 获取开始时间、执行类、执行方法
* @param joinPoint
*/
@Before("execution(* cn.phl.aop.controller.*.*(..))")
public void doBefore(JoinPoint joinPoint) throws NoSuchMethodException {
//访问时间
visitTime = new Date();
//执行类
clazz = joinPoint.getTarget().getClass();
//执行方法
String methodName = joinPoint.getSignature().getName();
//判断是否有参数
Object[] args = joinPoint.getArgs();
if(args == null && args.length==0){//无参数的情况
method = clazz.getMethod(methodName);
}else {//有参数
//遍历获取所有参数的Class对象
Class[] argsClass = new Class[args.length];
for (int i = 0; i < args.length; i++) {
argsClass[i] = args[i].getClass();
}
method = clazz.getMethod(methodName,argsClass);
}
}
/**
* 后置通知:
* 获取执行时长、URL、IP地址、Username
*/
@Autowired
private HttpServletRequest request;//请求对象——获取IP地址
@Autowired
private ILogService logService;//日志的Service层——保存对象
@After("execution(* cn.phl.aop.controller.*.*(..))")
public void doAfter(JoinPoint joinPoint){
//1.获取执行时长
Long time = new Date().getTime() - visitTime.getTime();
//2.URL
//2.1:获取类的注解对象
RequestMapping classAnnotation = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
RequestMapping methodAnnotation = (RequestMapping) method.getAnnotation(RequestMapping.class);
String[] classValue = null;
if (classAnnotation != null){//类上存在该注解
classValue = classAnnotation.value();
}
String[] methodValue = null;
if (methodAnnotation !=null){//方法上存在该注解
methodValue = methodAnnotation.value();
}
url = classValue[0]+methodValue[0];//拼接url(只涉及了类和方法上的url拼接)
//3.获取IP地址
ip = request.getRemoteAddr();
//4.获取当前登录用户——通过SecurityContext/Session/Zuul等的上下文对象获取
//5.获取自定义注解的值
LogDescribe logDescribe = method.getAnnotation(LogDescribe.class);
String methodDescribe = "";
if (logDescribe != null){//有该注解
methodDescribe = "-"+logDescribe.describe();
}
//6.调用Log的Service层进行保存
Log log = new Log();
log.setExecutionTime(time);
log.setIp(ip);
log.setMethod(method.getName()+methodDescribe);
log.setUrl(url);
log.setVisitTime(visitTime);
logService.save(log);
}
三、结束语
只是写了一个小demo测试一下,记录下最近学习得所得,如有不妥欢迎指正。也会根据后期自己的一些小心得进行修缮。