SpringBoot - AOP的简单运用

前言

平时项目开发过程中,记录操作日志是必不可少的,然而使用传统的方法,即在业务代码中加入记录日志这一部分代码,会导致这项工作很繁琐,而且都是在做一些重复性工作,还增加大量冗余代码,这种方式记录日志肯定是不可行的。

利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。(来自百度百科!)

AOP主要可以用来进行日志记录,性能统计,安全控制,事务处理,异常处理等等。

实现

1、添加Maven依赖

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-aop</artifactId>
	</dependency>

	<dependency>
		<groupId>cn.hutool</groupId>
		<artifactId>hutool-all</artifactId>
		<version>5.7.20</version>
	</dependency>

	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
	</dependency>
</dependencies>

2、编写几个需要用到的类

  • 1:日志状态枚举
/**
 * @author luoweijie
 * @date 2022/02/25
 */
public enum LogStatus {
    /**
     * 日志状态枚举
     */
    SUCCESS(0),
    FAILED(1);

    private final Integer status;

    LogStatus(Integer status) {
        this.status = status;
    }
}
  • 2:操作类型枚举
/**
 * @author luoweijie
 * @date 2022/02/25
 */
public enum LogType {
    /**
     * 日志类型枚举
     */
    TEST("测试"),
    OTHER("其他日志");

    private final String value;

    LogType(String value) {
        this.value = value;
    }
}
  • 3:工具类
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * @author luoweijie
 * @date 2022/02/25
 */
public abstract class HttpContextUtils {

    public static HttpServletRequest getRequest() {
        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
    }
}
  • 3:测试实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author luoweijie
 * @date 2022/02/25
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;

    private String username;

    private String password;
}

3、自定义Log注解

注解内容可自行拓展~~

import com.xm.model.enums.LogType;

import java.lang.annotation.*;

/**
 * @author luoweijie
 * @date 2022/02/25
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    LogType type() default LogType.OTHER;

    boolean saveRequestParams() default false;
}

4、编写切面类

import cn.hutool.json.JSONUtil;
import com.xm.annotation.Log;
import com.xm.model.enums.LogStatus;
import com.xm.utils.HttpContextUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

/**
 * @author luoweijie
 * @date 2022/02/25
 */
@Aspect
@Component
public class LogAspect {
    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    private Long startTime;

    @Pointcut("@annotation(com.xm.annotation.Log)")
    public void pointCut() {
    }

    @Before("pointCut()")
    public void before() {
        startTime = System.currentTimeMillis();
    }

    @AfterThrowing("pointCut()")
    public void afterThrowing(JoinPoint joinPoint) {
        handleLog(joinPoint, LogStatus.FAILED);
    }

    @AfterReturning("pointCut()")
    public void afterReturning(JoinPoint joinPoint) {
        handleLog(joinPoint, LogStatus.SUCCESS);
    }

    @Async
    public void handleLog(JoinPoint joinPoint, LogStatus logStatus) {
        // get request
        HttpServletRequest request = HttpContextUtils.getRequest();

        // get execution time(ms)
        long time = System.currentTimeMillis() - startTime;

        // get method
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();

        // get log annotation
        Log log = method.getDeclaredAnnotation(Log.class);
        if (log.saveRequestParams()) {
            // do something..
            logger.info("execution time : {}ms", time);
            logger.info("request url : {}", request.getRequestURL());
            logger.info("request params : {}", parseToJson(method.getParameters(), joinPoint.getArgs()));

            // save to database
        }
    }

    /**
     * 将参数转为JSON格式
     *
     * @param parameters 参数属性
     * @param args       参数值
     * @return JSON
     */
    private String parseToJson(Parameter[] parameters, Object[] args) {
        logger.info(parameters.length + ", " + args.length);
        StringBuffer sb = new StringBuffer("{ \"params\": ");
        Map<String, Object> paramsMap = new HashMap<>(parameters.length);
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            Object arg = args[i];
            logger.info("param type: {}, param name : {}, param value : {}", parameter.getType(), parameter.getName(), arg);
            paramsMap.put(parameter.getName(), arg);
        }
        return sb.append(JSONUtil.toJsonStr(paramsMap)).append("}").toString();
    }
}

5、写个Controller进行测试

import com.xm.annotation.Log;
import com.xm.model.entity.User;
import com.xm.model.enums.LogType;
import org.springframework.web.bind.annotation.*;

import java.util.concurrent.TimeUnit;

/**
 * @author luoweijie
 * @date 2022/02/25
 */
@RestController
@RequestMapping("log")
public class LogController {

    @Log(type = LogType.OTHER, saveRequestParams = true)
    @PostMapping("test")
    public String testLog(@RequestParam("code") String code,
                          @RequestBody User user) throws InterruptedException {
        TimeUnit.SECONDS.sleep(2);
        return "test log annotation success!";
    }
}

启动应用访问

不出意外可以看到如下信息:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值