一、介绍
需求:
使用AOP切面,将用户所有的操作行为记录下来。
二、pom依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<!--阿里巴巴json-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<!-- aspectjrt -->
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.3</version>
</dependency>
<!-- aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<!-- Mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--SpringDataJPA-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
三、自定义注解
自定义一个自己的注解,设置一个字段定义为用户的操作行为;
package com.lsh.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author :LiuShihao
* @date :Created in 2021/2/26 12:09 下午
* @desc :
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
/**
* 操作记录
* @return
*/
String value() default "";
}
四、切面类
切面会在添加MyLog
注解的方法执行后执行。
切面会获取 调用当前方法的时间、IP地址、用户名(IP和用户名都是从Session域中获取的)、以及方法接收的参数。
package com.lsh.aspect;
import com.lsh.annotation.MyLog;
import com.lsh.entity.LogRecord;
import com.lsh.repository.LogRecordRepository;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
/**
* @author :LiuShihao
* @date :Created in 2021/2/26 12:12 下午
* @desc :
* @Aspect 声明一个切面类
* @Component 注册到Spring容器
*/
@Aspect
@Component
public class MyAspect {
@Autowired
LogRecordRepository logRecordRepository;
/**
* 后置通知
* 注意 : 注解的切入点表达式
* 需求 : 记录操作时间,操作人,操作的方法,操作的描述,操作的ip 数据
* 时间 : new Date()
* 人,ip : 需要请求对象
* 方法 : 通过AOP的切入点得到方法名
* JoinPoint 就是目标方法对象
* 描述 : 通过反射读取注解
* ===================================
* 以上属性可以设计成数据库的一种表,用于记录日志信息.
* 对应的,在创建一个java实体类用于封装数据
* @param joinPoint
*/
@After("@annotation(com.lsh.annotation.MyLog)")
public void afterMethod2(JoinPoint joinPoint) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
LogRecord logRecord = new LogRecord();
// 操作时间
String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("方法调用时间 : "+time);
// 使用Spring提供的方法,获得HttpServletRequest对象
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String ip = request.getRemoteAddr();
System.out.println("方法调用IP地址 : "+ip);
// 操作人用户名 人名是从session中获取的值
String name = (String) request.getSession().getAttribute("username");
System.out.println("方法调用者 : "+name);
String requestURL = request.getRequestURL().toString();
String requestURI = request.getRequestURI();
System.out.println("URL :"+requestURL+"; "+" URI: "+requestURI);
// 使用joinPoint得到方法名
String methodName = joinPoint.getSignature().getName();
System.out.println("调用的方法名:"+methodName);
Method[] desc = joinPoint.getTarget().getClass().getMethods();
String editContent = "";
List<Method> methods = Arrays.asList(desc);
for (Method method : methods) {
// System.out.println("方法名:"+method.getName());
if (method.getName().equals(methodName)){
editContent = method.getAnnotation(MyLog.class).value();
}
}
//通过反射 获得切入点的目标类的class对象的这个方法上的我们的自定义的MyLog注解的value
System.out.println("调用方法操作内容:"+editContent);
//获得 方法的参数
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.println(arg.toString());
Class<?> aClass = arg.getClass();
Field[] fields = aClass.getFields();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
//注意:设置字段可访问, 否则无法访问private修饰的变量值
declaredField.setAccessible(true);
//注意:这里需要指定对象 才能获取对应的值
// System.out.println("字段名2:"+declaredField.getName()+"--》"+declaredField.get(arg));
if ("operator".equals(declaredField.getName())){
logRecord.setOperator((String)declaredField.get(arg));
}
}
}
// System.out.println("方法请求的参数:"+s);
logRecord.setUrl(requestURL);
logRecord.setCreateTime(time);
logRecord.setIpAddress(ip);
logRecord.setEditContent(editContent);
logRecordRepository.save(logRecord);
}
}
五、实体类
package com.lsh.entity;
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
/**
* (LogRecord)实体类
*
* @author LiuShihao
* @since 2021-04-15 09:15:34
*/
@Data
@Entity
@Table(name = "log_record")
public class LogRecord implements Serializable {
private static final long serialVersionUID = 278820648583560379L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private Integer flag;
/**
* 创建时间
*/
private String createTime;
/**
* 更新时间
*/
private String updateTime;
/**
* 操作人
*/
private String operator;
private String editContent;
private String ipAddress;
private String username;
private String url;
}
六、Controller
MyLog
注解加载要记录的方法上即可,应该是写在Server层的方法上,简单演示一下,没有写业务层。
package com.lsh.controller;
import com.lsh.annotation.MyLog;
import com.lsh.entity.LogRecord;
import com.lsh.service.MyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
/**
* @author :LiuShihao
* @date :Created in 2021/2/26 12:32 下午
* @desc :
*/
@Slf4j
@RestController
@RequestMapping("/mylog")
public class MyController {
@Autowired
MyService myService;
@PostMapping("testAspect")
public Map<String,Object> testAspect(@RequestBody LogRecord logRecord){
HashMap<String, Object> resp = new HashMap<>();
try {
resp.put("code",0000);
resp.put("msg","SUCCESS");
System.out.println("testAspect入参:"+logRecord);
myService.test1(logRecord);
}catch (Exception e){
e.printStackTrace();
System.out.println("异常:"+e.getMessage());
resp.put("code",9999);
resp.put("msg","FAIL");
}
return resp;
}
}
测试