0
点赞
收藏
分享

微信扫一扫

AOP基础

兽怪海北 2024-11-04 阅读 13

概述

AOP:Aspect Oriented Programming面向切面编程、面向方面编程。其实就是面向特定方法编程。

最主流的实现是动态代理

AOP基础_spring

Spring AOP快速入门:统计各个业务层方法执行耗时

AOP依赖

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

编写AOP程序

package com.example.springbootmybatis.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Slf4j

@Component
@Aspect//这个注释表示这个java文件为aop文件
public class TimeAspect {



    @Around("execution(* com.example.springbootmybatis.service.*.*(..))")//作用在哪些方法上
    // 根据实际需求修改切入点表达式,第一个星表示类名或者接口名,第二个星代表所有的方法名,可以把service包换成其他包名
    // (..)表示任意参数
    public Object recordTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long begin = System.currentTimeMillis();
        Object result = proceedingJoinPoint.proceed(); // 调用原始方法
        long end = System.currentTimeMillis();
        log.info(proceedingJoinPoint.getSignature() + " 执行耗时: {}ms", end - begin);//方法名+参数+耗时
        return result;
    }
}

AOP应用场景

记录操作日志,极限控制,事务管理

优点

代码无侵入、减少重复代码、提高开发效率、维护方便

AOP核心概念

连接点:JoinPoint

通知:Advice

切入点:PointCut

切面:Aspect

目标对象:Target

AOP基础_java_02

AOP进阶

1.通知类型

@Around

AOP基础_AOP_03

package com.example.springbootmybatis.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Slf4j
@Component
@Aspect
public class Aspect1 {



    // 环绕通知
    @Around("execution(* com.example.springbootmybatis..*(..))")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("环绕通知:方法开始");
        Object result = joinPoint.proceed();
        log.info("环绕通知:方法结束");
        return result;
    }

    // 前置通知
    @Before("execution(* com.example.springbootmybatis..*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        log.info("前置通知:方法调用前");
    }

    // 后置通知
    @After("execution(* com.example.springbootmybatis..*(..))")
    public void afterAdvice(JoinPoint joinPoint) {
        log.info("后置通知:方法调用后");
    }

    // 返回后通知
    @AfterReturning(pointcut = "execution(* com.example.springbootmybatis..*(..))", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        log.info("返回后通知:方法返回 " + result);
    }

    // 异常后通知
    @AfterThrowing(pointcut = "execution(* com.example.springbootmybatis..*(..))", throwing = "error")
    public void afterThrowingAdvice(JoinPoint joinPoint, Throwable error) {
        log.error("异常后通知:异常 " + error);
    }
}

pointcut:解决代码重复。

如果想在其他切面类中引用,改为public即可

@Pointcut("execution(* com.example.springbootmybatis..*(..))")
public void pointcut() {}
    // 环绕通知
    @Around("pointcut()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("环绕通知:方法开始");
        Object result = joinPoint.proceed();
        log.info("环绕通知:方法结束");
        return result;
    }

AOP基础_java_04

2.通知顺序

如果多个类下的通知完全一样,那么顺序是按照类目字母排序。

AOP基础_spring_05

@Order(1)

3.切入点表达式

AOP基础_spring_06

1.execution

AOP基础_java_07

包名和类名不建议省略,因为会导致范围过大,可能会运行多次

AOP基础_AOP_08

//@Pointcut("execution(* com.example.springbootmybatis..*(..))")// .. 的意思是可以匹配 com.example.springbootmybatis 包及其所有子包下的所有类和方法。
@Pointcut("execution(* com.example.springbootmybatis.*service.delete*(..))")//匹配以service结尾,delete开头的方法

使用或连接两个方法

@Pointcut("execution(* com.example.springbootmybatis.service.DeptService.list()) || " +
          "execution(* com.example.springbootmybatis.service.DeptService.delete(java.lang.Integer))")

AOP基础_AOP_09

2.annotation

AOP基础_spring_10

自定义注解

package com.example.springbootmybatis.aop;

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

@Target(ElementType.METHOD)//注解的位置
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)//什么时候执行

public @interface MyLog {
}

@Pointcut("@annotation(com.example.springbootmybatis.aop.MyLog)")

4.连接点

AOP基础_spring_11

package com.example.springbootmybatis.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class Aspect2 {



    @Pointcut("execution(* com.example.springbootmybatis..*(..))")
    private void pt() {}

    @Before("pt()")
    public void before() {
        log.info("Aspect2 ... before ...");
    }

    @Around("pt()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("Aspect2 around before ...");

        // 1. 获取目标对象的类名
        String className = joinPoint.getTarget().getClass().getName();
        // 2. 获取目标方法的方法名
        String methodName = joinPoint.getSignature().getName();
        // 3. 获取目标方法运行时传入的参数
        Object[] args = joinPoint.getArgs();
        // 打印类名、方法名和参数
        log.info("Executing " + className + "." + methodName + " with arguments: " + java.util.Arrays.toString(args));

        // 4. 放行,执行目标方法
        Object result = joinPoint.proceed();

        // 5. 获取目标方法的返回值
        log.info("MyAspect8 around after ... Result: " + result);

        return result;
    }
}

AOP案例

AOP基础_AOP_12

AOP基础_spring_13

新建表

create table operate_log(
    id int unsigned primary key auto_increment comment 'ID',
    operate_user int unsigned comment '操作人ID',
    operate_time datetime comment '操作时间',
    class_name varchar(100) comment '操作的类名',
    method_name varchar(100) comment '操作的方法名',
    method_params varchar(1000) comment '方法参数',
    return_value varchar(2000) comment '返回值',
    cost_time bigint comment '方法执行耗时,单位:ms'
) comment '操作日志表';

实体类

package com.example.springbootmybatis.pojo;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {
    private Integer id; // ID
    private Integer operateUser; // 操作人ID
    private LocalDateTime operateTime; // 操作时间
    private String className; // 操作类名
    private String methodName; // 操作的方法名
    private String methodParams; // 操作方法参数
    private String returnValue; // 操作方法返回值
    private Long costTime; // 操作耗时
}

自定义注解

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)
public @interface Log {
}

实现类

package com.example.springbootmybatis.aop;

import com.alibaba.fastjson.JSONObject;
import com.example.springbootmybatis.mapper.OperateLogMapper;
import com.example.springbootmybatis.pojo.OperateLog;
import com.example.springbootmybatis.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Arrays;

@Aspect
@Component
public class LogAspect {

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private OperateLogMapper operateLogMapper;

    @Around("@annotation(com.example.springbootmybatis.anno.Log)")
    public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取用户ID
        String jwt = request.getHeader("token");
        Claims claims = JwtUtils.parseJwt(jwt);
        Integer operateUser = (Integer) claims.get("id");

        // 操作时间
        LocalDateTime operateTime = LocalDateTime.now();

        // 操作类名
        String className = joinPoint.getTarget().getClass().getName();

        // 操作方法名
        String methodName = joinPoint.getSignature().getName();

        // 操作方法参数
        Object[] args = joinPoint.getArgs();
        String methodParams = Arrays.toString(args);

// 记录开始时间
        long begin = System.currentTimeMillis();

// 调用目标方法运行
        Object result = joinPoint.proceed();
        long end = System.currentTimeMillis();

// 方法返回值
        String returnValue = JSONObject.toJSONString(result);

// 计算耗时
        Long costTime = end - begin;

// 记录操作日志
        // 创建 OperateLog 对象
        OperateLog operateLog = new OperateLog(
                null,
                operateUser,
                operateTime,
                className,
                methodName,
                methodParams,
                returnValue,
                costTime
        );

// 插入日志到数据库
        log.info("AOP操作日志:{}", operateLog);
        operateLogMapper.insert(operateLog);
        return result;


    }
}

在增删改的控制层加上注解Log

AOP基础_AOP_14

举报

相关推荐

0 条评论