【Java】自定义注解和AOP切面的使用

前言

我们在开发的过程中,一般都需要对方法的入参进行打印,或者Debug调试的时候我们要查看方法入参的参数是否数量和数据正确性。

一般我们需要知道请求的参数接口路径请求ip

但是考虑以后项目上线BUG排查的问题,最好的方式就是使用切面的方式来记录每个方法执行时要保存的日志处理,那么下面我们来实现一个使用自定义注解的方式来对每个请求的方法上进行日志存储

AOP切面:对某个方法进行增强处理,例如在某个方法执行前或者执行后进行操作。

案例

首先我们看一个controller接口

import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
@RequestMapping("/user/")
public class TestController {

    @PostMapping("getUserById")
    public String getUserById(@RequestBody User user) {
        log.info("/user/getUserById params:{}", user.toString());
        //执行代码逻辑...
        return "请求成功";
    }
}

这种一般没什么问题,一般我们测试的时候都可以这样来写,但如果有很多的方法,那么每个方法都这样写,显然很是繁琐,那么我们通过下面的方式来实现当进入方法前打印请求的一些信息

实现

我们自定义一个注解,名为Itboy

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)//ElementType.METHOD表示为该注解在方法上添加
public @interface Itboy {
}

然后需要用到一些依赖,这些是使用AOP的依赖,我们提前引入一下

<dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.14</version>
</dependency>
<dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.9.19</version>
</dependency>

然后我们定义一个切面类,名为ItboyAspect
注:这篇文章我使用的System打印方式,如果需要保存日志,换为对应的logger.info()即可。

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
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.aspectj.lang.annotation.Pointcut;
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 java.text.SimpleDateFormat;
import java.util.Date;

@Component
@Aspect
@Slf4j
public class ItboyAspect {
    //@Pointcut:为切入点,切入到Itboy这个注解上面
    @Pointcut("@annotation(com.mayikt.demo.Itboy)")
    public void itboyAspect() {}

    //@Before为在进入切点之前自动执行Before中的逻辑(进入之前的前提是方法上需要有我们设置的自定注解)
    @Before("itboyAspect()")
    public void beforeItboy(JoinPoint joinPoint) {
        //获取本次请求
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //获取到方法名
        String methodName = joinPoint.getSignature().getName();
        System.err.println("======================================方法:" + methodName + "() 开始======================================");
        //执行时间
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = new Date();
        String time = sdf.format(date);
        System.err.println("时间                 : " + time);
        //请求URL
        System.err.println("URL                 : " + request.getRequestURL());
        //请求方法
        System.err.println("HTTP Method         : " + methodName);
        //打印controller全路径和执行方法
        System.err.println("Class Method        : " + joinPoint.getSignature().getDeclaringTypeName() + "." + methodName);
        //请求IP
        System.err.println("IP                  : " + request.getRemoteHost());
        //请求入参
        System.err.println("Requert params      : " + JSON.toJSONString(joinPoint.getArgs()));
    }
	
	//后置通知
	@After("itboyAspect()")
    public void afterItboy(JoinPoint joinPoint) {
        //获取到方法名
        String methodName = joinPoint.getSignature().getName();
        System.err.println("======================================方法:" + methodName + "() 结束======================================");
    }
}

然后我们只需要在需要保存的方法上添加@Itboy注解即可

	@Itboy
    @PostMapping("selectUserList")
    public String getUserById(@RequestBody User user) {
        log.info("/user/selectUserList params:{}", user.toString());

        //执行代码逻辑...
        return "请求成功";
    }

最后我们来看一下效果

2023-04-02 14:21:29.033  INFO 5912 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
======================================方法:getUserById() 开始======================================
时间                 : 2023-04-02 14:21:29
URL                 : http://127.0.0.1:8080/user/selectUserList
HTTP Method         : getUserById
Class Method        : com.mayikt.demo.TestController.getUserById
IP                  : 127.0.0.1
Requert params      : [{"address":"华东","age":18,"id":10010,"name":"贾强"}]
======================================方法:getUserById() 结束======================================
2023-04-02 14:21:29.171  INFO 5912 --- [nio-8080-exec-1] com.mayikt.demo.TestController           : /user/selectUserList params:User(id=10010, name=贾强, age=18, address=华东)

总结

使用上面这种方式,减轻了我们自己手动日志打印的繁琐,而且配置也相对于比较简单,如果有其他需求是比较频繁的操作的话,我们就可以使用AOP切面的方式来完成。

肥肠好用

你可能感兴趣的:(java,spring,开发语言)