SpringBoot整合AOP和自定义注解保存操作日志实战

一、准备部分:
创建项目:
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测试一下,记录下最近学习得所得,如有不妥欢迎指正。也会根据后期自己的一些小心得进行修缮。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值