自定义注解 + AOP 记录接口访问日志
发布日期:2021-05-08 13:47:10 浏览次数:25 分类:精选文章

本文共 3775 字,大约阅读时间需要 12 分钟。

AOP(面向切面编程)简明介绍

一、AOP的基本概念

1. AOP是什么

AOP(Aspect Oriented Programming,面向切面编程)是一种通过预编译和运行时动态代理,在不修改源代码的情况下为程序添加功能的技术。

实现方式主要有两种:JDK动态代理和CGLIB代理。前者基于接口,后者基于子类。两种代理方式各有优劣,选择时需根据实际需求权衡。

2. AOP可以实现的功能

  • 统计接口访问次数
  • 增强功能:在不改动代码的情况下,为接口增加额外功能

3. 切面执行顺序

正常执行顺序:方法执行→切面1→切面2→方法返回

异常执行顺序:方法执行→切面1→异常处理→切面2→方法抛出异常

4. AOP注解

  • @Aspect:定义切面,通常用于标注切入点和通知逻辑
  • @Pointcut:定义切入点,用于指定切面执行的连接点
  • Advice:通知,用于在切入点处执行增强逻辑
  • JointPoint:连接点,表示程序运行中的某个阶段点(如方法调用、异常抛出等)
  • Advisor:增强,包含Pointcut和Advice的综合体,描述增强的位置和方式

5. 自定义注解

在AOP中,自定义注解可以帮助更灵活地管理切面。常用的注解包括:

  • @Target:描述注解作用的目标范围,常用值有:METHOD、PACKAGE、PARAMETER、TYPE
  • @Retention:指定注解保留的时间范围,常用值有:SOURCE、CLASS、RUNTIME
  • @Documented:标记注解为文档化,确保注解信息能被生成 javadoc

二、AOP实战代码

1. 依赖引入

org.aspectj  aspectjrt  1.9.4  org.aspectj  aspectjweaver  1.9.4  cglib  cglib  3.2.12

2. AOP示例

通过以下示例可以更直观地理解AOP的实际应用:

import com.example.demo.module.annotation.SystemLog;import com.example.demo.module.utils.AtomicCounter;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.*import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;@Component@Aspectpublic class ApiLogAopAction {    private static final Logger LOGGER = LoggerFactory.getLogger(ApiLogAopAction.class);    ThreadLocal
startTime = new ThreadLocal<>(); @Autowired HttpServletRequest request; @Pointcut("@annotation(com.example.demo.module.annotation.SystemLog)") public void log() {} @Before("log()") public void doBefore(JoinPoint joinPoint) { startTime.set(System.currentTimeMillis()); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); SystemLog annotation = signature.getMethod().getAnnotation(SystemLog.class); AtomicCounter.init(annotation.module(), request.getRequestURI()); AtomicCounter.getInstance().increaseVisit(request.getRequestURI()); } @AfterReturning(returning = "returnVal", pointcut = "log()") public void doAfterReturning(JoinPoint joinPoint, Object returnVal) { LOGGER.info("URI:[{}], 耗费时间:[{}] ms, 访问次数:{}", request.getServletPath(), System.currentTimeMillis() - startTime.get(), AtomicCounter.getInstance().increaseSuccess(request.getRequestURI())); } @AfterThrowing(pointcut = "log()") public void doAfterThrowing(JoinPoint joinPoint) { LOGGER.info("接口访问失败,URI:[{}], 耗费时间:[{}] ms", request.getServletPath(), AtomicCounter.getInstance().increaseFail(request.getRequestURI())); } @After("log()") public void doAfter(JoinPoint joinPoint) { LOGGER.info("End.{}", AtomicCounter.getInstance().getValue(request.getRequestURI())); }}

3. 自定义注解示例

import java.lang.annotation.*;/** * @Title SystemLog * @Description 接口日志注解 * @author ACGkaka * @date 2021/4/1 11:36 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface SystemLog {    String module() default "";    String method() default "";    String operateType() default "OTHER";    String logType() default "0";}

4. 定义接口,进行测试

import com.example.demo.module.annotation.SystemLog;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class DemoController {    @GetMapping("/index")    @SystemLog(module = "首页", method = "hello", operateType = "SELECT", logType = "1")    public String index() {        return "

Hello World.

"; } @GetMapping("login") @SystemLog(module = "首页", method = "login", operateType = "LOGIN", logType = "1") public String login() { int i = 1 / (Math.random() > 0.5 ? 0 : 1); return "测试报错的AOP方法"; }}

参考文章

  • @Pointcut()的execution、@annotation等参数说明
  • Java 自定义注解及使用场景
  • 上一篇:Java实现并发请求
    下一篇:从零开始搭建springboot-dubbo

    发表评论

    最新留言

    能坚持,总会有不一样的收获!
    [***.219.124.196]2025年04月19日 02时09分37秒