SpringBoot自定义注解+AOP实现用户操作行为记录

一、介绍

需求:
使用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;
    }

}


测试

在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,关于SpringBoot自定义注解AOP的问题,我可以为您提供一些基本的介绍和示例代码。 首先,AOP(Aspect-Oriented Programming)是一种编程范式,它可以在不修改业务逻辑代码的情况下,对应用程序进行横切关注点的切面处理。而Spring AOP作为Spring框架的一部分,提供了一种基于代理模式的AOP实现。 在使用Spring AOP的过程中,自定义注解可以作为切点表达式的一部分,通过对注解的解析,实现对被注解的方法或类的切面处理。下面是一个简单的示例代码,演示如何通过自定义注解实现对方法的AOP处理: 首先,定义一个自定义注解: ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyAnnotation { String value() default ""; } ``` 然后,在需要被拦截的方法上添加该注解: ```java @Service public class MyService { @MyAnnotation("myAnnotation") public void doSomething() { System.out.println("do something..."); } } ``` 接下来,使用AspectJ的@Aspect注解定义一个切面类,并在该类中定义一个切点,用于匹配被@MyAnnotation注解的方法: ```java @Aspect @Component public class MyAspect { @Pointcut("@annotation(com.example.demo.annotation.MyAnnotation)") public void myAnnotationPointcut() {} @Before("myAnnotationPointcut()") public void beforeMyAnnotation() { System.out.println("before myAnnotation..."); } } ``` 最后,启动SpringBoot应用程序,调用MyService的doSomething方法,就可以看到输出结果: ```java before myAnnotation... do something... ``` 以上就是一个简单的SpringBoot自定义注解AOP的示例。通过使用自定义注解,可以更加方便地实现对应用程序的切面处理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Liu_Shihao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值