AOP的原理与应用:以插入数据库中修改时间和监控方法性能为例。

一、AOP的概念

1.原理

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程思想,用于将通用的横切关注点(如日志记录、事务管理、安全性等)与核心业务逻辑分离。通过 AOP,可以在不修改业务逻辑代码的情况下动态地向程序中添加功能。

2.1 切面(Aspect):
  • 包含切点和通知的模块化关注点,例如日志、事务。

2.2 通知(Advice):
  • 定义切面在目标方法执行时所采取的操作:

    • @Before:在方法执行前执行。

    • @AfterReturning:在方法返回后执行。

    • @AfterThrowing:在方法抛出异常时执行。

    • @After:无论方法是否成功执行,都会执行。

    • @Around:环绕通知,可以控制方法执行前后行为。

2.3 切点(Pointcut):
  • 定义拦截的范围和条件,比如某些包下的类或特定注解。

2.4 目标对象(Target):
  • 被 AOP 代理的对象。

2.5 织入(Weaving):
  • 将切面逻辑应用到目标对象上的过程。

2.6 代理对象(Proxy):
  • 目标对象的增强版本,通过代理调用目标方法。

二、使用步骤

2.1例如:以向数据库中插入或更新字段时需要插入修改时间为例:
2.1.1 定义横切关注点:注解

定义一个注解(@AutoFill),用来标记需要切面增强的方法

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
    OperationType value(); // 数据库操作类型,例如 INSERT、UPDATE
}

2.1.2 定义切面类

 切面类中定义切点和通知逻辑。

@Aspect
@Component
@Slf4j
public class AutoFillAspect {
​
    // 定义切点,拦截带有 @AutoFill 注解的方法
​
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut() {
        //关于@PointCut的解释:(* com.sky.mapper.*.*(..))
        // 1. execution是切点表达式,定义拦截范围
        //    1.1 第一个*(在com之前的*):
        //          表示方法的返回值,*表示拦截所有返回值的类型,比如void、string、int等
        //    1.2 com.sky.mapper.*.*(..):
        //          具体意义为:com.sky.mapper包下的所有类(注意不是所有子类,如果想包括子包,可以用..)的所有方法,
        //          (..)表示任意参数个数和类型
        //
        // 2. @annotation(com.sky.annotation.AutoFill):
        //    2.1 @annotation:
        //          表示方法上需要带有指定注解
        //    2.2 (com.sky.annotation.AutoFill)中的内容:
        //          是限定注解的全称限定名
    }
​
    // 定义通知,目标方法执行前调用
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint) {
         /**
         * JoinPoint JoinPoint 是 Spring AOP 提供的接口,用于在切面中获取被拦截方法的上下文信息,
         *      包括方法签名(MethodSignature)、方法参数等。
         *      因此,JoinPoint 是 Spring AOP 的特定功能,不能在非 Spring 框架中直接使用。
         */
        log.info("公共字段进行补充...");
        // 获取方法上的注解
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
        OperationType operationType = autoFill.value();
​
        // 获取方法的参数,执行反射赋值逻辑
        Object[] args = joinPoint.getArgs();
        Object entity = args[0]; // 假设实体对象在第一个参数
        //... coding剩余逻辑
    }
}

2.1.3. 使用 AOP 注解

将切面增强逻辑应用到 Mapper 方法。

public interface EmployeeMapper {
​
    @AutoFill(OperationType.INSERT)
    void insert(Employee employee);
​
    @AutoFill(OperationType.UPDATE)
    void update(Employee employee);
}

2.1.4. Spring 容器配置

Spring Boot 项目中默认开启 AOP 支持,只需引入依赖即可:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.2 再例如:使用AOP监控某一方法性能:

注意在这里没有自定义注解,是因为希望该功能作用在每一个方法上,所以不需要自定义注解来指定哪个方法需要执行该功能。

2.2.1. 定义切面类
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;
​
@Aspect
@Component
@Slf4j
public class PerformanceMonitorAspect {
​
    // 切点:拦截 com.example.service 包下的所有方法
    @Around("execution(* com.example.service.*.*(..))")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法名称
        String methodName = joinPoint.getSignature().getName();
​
        // 记录开始时间
        long startTime = System.currentTimeMillis();
​
        // 执行目标方法
        Object result;
        try {
            result = joinPoint.proceed();
        } finally {
            // 记录结束时间
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;
​
            // 打印日志
            log.info("Method [{}] executed in {} ms", methodName, duration);
        }
​
        return result;
    }
}

2.2.2. 使用AOP注解

定义一个服务类,用于测试切面功能。

import org.springframework.stereotype.Service;
​
@Service
public class SampleService {
​
    public String performTask() {
        // 模拟业务逻辑
        try {
            Thread.sleep(500); // 模拟耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Task Completed";
    }
​
    public void anotherTask() {
        // 模拟另一个任务
        try {
            Thread.sleep(300); // 模拟耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

2.2.3. 容器配置

确保项目中已引入 Spring AOP 相关依赖:

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

三、其它细节

3.1 Signature 和 MethodSignature:

如果需要访问方法的参数、返回值类型、注解等更详细的信息,必须使用 MethodSignature

3.1.1 Signature:

Signature 是一个通用接口,表示连接点(JoinPoint)签名的基本信息,适用于方法、构造器等。

常用方法:

  • String getName() 返回方法或构造器的名称。

  • String getDeclaringTypeName() 返回声明该签名的类或接口的全限定名。

  • Class<?> getDeclaringType() 返回声明该签名的类或接口的 Class 对象。

Signature signature = joinPoint.getSignature();
System.out.println("Name: " + signature.getName());  // 方法名
System.out.println("Declaring Type: " + signature.getDeclaringTypeName());  // 全类名
3.1.2 MethodSignature

MethodSignature是Signature 的子接口,提供了专门用于描述方法的扩展信息。

常用方法:

  • 继承自 Signature 的方法。

  • Method getMethod() 返回被拦截的方法对象(java.lang.reflect.Method)。

  • Class<?>[] getParameterTypes() 返回方法参数的类型数组。

  • Type[] getGenericParameterTypes() 返回方法参数的泛型类型数组。

  • Class<?> getReturnType() 返回方法的返回值类型。

  • Type getGenericReturnType() 返回方法的泛型返回值类型。

  • String[] getParameterNames() 返回方法参数的名称数组(需要 AOP 配置正确支持参数名)。

MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
System.out.println("Method: " + methodSignature.getMethod());  // 方法对象
System.out.println("Return Type: " + methodSignature.getReturnType());  // 返回值类型
System.out.println("Parameter Names: " + Arrays.toString(methodSignature.getParameterNames()));  // 参数名

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值