Spring从入门到精通(三)之AOP

这是学习黑马程序SSM的课程笔记,这小节主要是AOP的介绍
原视频地址:https://www.bilibili.com/video/BV1Fi4y1S7ix

AOP

简介

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,它用于分离横切关注点(cross-cutting concerns)与主要业务逻辑的模块。横切关注点是那些存在于应用程序各处的功能,如日志记录、事务管理、安全性检查、性能优化等。AOP的主要目标是提高代码的模块化性,降低重复性代码,并提高代码的可维护性。

AOP的核心思想是将横切关注点抽象为切面(Aspect),然后将切面与主要业务逻辑代码进行解耦。这样,你可以在不修改主要业务逻辑代码的情况下,轻松地添加、修改或删除横切关注点。你的源代码不需要改,我就可以为它增加功能。无侵入式。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

AOP工作流程

在这里插入图片描述
JDK代理(Java Dynamic Proxy)是Java语言中的一种动态代理机制,它允许在运行时创建代理类来代理实现了特定接口的目标对象。这种代理机制通常用于AOP(Aspect-Oriented Programming)和其他与横切关注点(cross-cutting concerns)相关的任务,如日志记录、事务管理、权限控制等。

JDK代理主要涉及以下两个核心组件:

  1. InvocationHandler(调用处理器):这是一个接口,通常需要用户实现。它定义了代理对象的方法调用处理逻辑。当代理对象的方法被调用时,实现了InvocationHandler的类的invoke方法会被调用,从而允许开发人员在方法调用前后插入自定义的逻辑。

  2. Proxy类:这是Java标准库提供的一个类,用于在运行时创建代理类。开发人员可以使用Proxy.newProxyInstance方法来创建代理对象,该方法需要传递一个类加载器、一组接口和一个InvocationHandler。

下面是一个简单的示例,演示了如何使用JDK代理:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 定义接口
interface MyInterface {
    void doSomething();
}

// 实现InvocationHandler接口的代理处理器类
class MyInvocationHandler implements InvocationHandler {
    private final MyInterface target;

    public MyInvocationHandler(MyInterface target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在方法调用前可以插入自定义逻辑
        System.out.println("Before method invocation");
        Object result = method.invoke(target, args); // 调用目标对象的方法
        // 在方法调用后可以插入自定义逻辑
        System.out.println("After method invocation");
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        MyInterface realObject = new MyInterface() {
            @Override
            public void doSomething() {
                System.out.println("Real object is doing something.");
            }
        };

        // 创建代理对象
        MyInterface proxyObject = (MyInterface) Proxy.newProxyInstance(
            Main.class.getClassLoader(),
            new Class[] { MyInterface.class },
            new MyInvocationHandler(realObject)
        );

        // 通过代理对象调用方法
        proxyObject.doSomething();
    }
}

在上述示例中,我们定义了一个接口MyInterface和一个实现了InvocationHandler接口的代理处理器类MyInvocationHandler。然后,我们通过Proxy.newProxyInstance方法创建了一个代理对象,该代理对象实现了MyInterface接口,并在代理对象的方法调用前后插入了自定义的逻辑。

JDK代理是一种非常有用的技术,用于实现动态代理和AOP,它可以帮助开发人员在运行时创建代理对象,并在不修改原始类的情况下添加额外的功能。

AOP切入点表达式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

AOP通知类型

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码示例

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    @Pointcut("execution(int com.itheima.dao.BookDao.select())")
    private void pt2(){}

    //@Before:前置通知,在原始方法运行之前执行
//    @Before("pt()")
    public void before() {
        System.out.println("before advice ...");
    }

    //@After:后置通知,在原始方法运行之后执行
//    @After("pt2()")
    public void after() {
        System.out.println("after advice ...");
    }

    //@Around:环绕通知,在原始方法运行的前后执行
//    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before advice ...");
        //表示对原始操作的调用
        Object ret = pjp.proceed();
        System.out.println("around after advice ...");
        return ret;
    }

//    @Around("pt2()")
    public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before advice ...");
        //表示对原始操作的调用
        Integer ret = (Integer) pjp.proceed();
        System.out.println("around after advice ...");
        return ret;
    }

    //@AfterReturning:返回后通知,在原始方法执行完毕后运行,且原始方法执行过程中未出现异常现象
//    @AfterReturning("pt2()")
    public void afterReturning() {
        System.out.println("afterReturning advice ...");
    }

    //@AfterThrowing:抛出异常后通知,在原始方法执行过程中出现异常后运行
    @AfterThrowing("pt2()")
    public void afterThrowing() {
        System.out.println("afterThrowing advice ...");
    }
}

测量业务层接口执行方法

@Component
@Aspect
public class ProjectAdvice {
    //匹配业务层的所有方法
    @Pointcut("execution(* com.itheima.service.*Service.*(..))")
    private void servicePt(){}

    //设置环绕通知,在原始操作的运行前后记录执行时间
    @Around("ProjectAdvice.servicePt()")
    public void runSpeed(ProceedingJoinPoint pjp) throws Throwable {
        //获取执行的签名对象
        Signature signature = pjp.getSignature();
        String className = signature.getDeclaringTypeName();
        String methodName = signature.getName();

        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
           pjp.proceed();
        }
        long end = System.currentTimeMillis();
        System.out.println("万次执行:"+ className+"."+methodName+"---->" +(end-start) + "ms");
    }

}

AOP通知获取数据

在这里插入图片描述
JoinPoint 是AOP(Aspect-Oriented Programming)中的一个概念,它表示在程序执行过程中可以被拦截的点。在Spring框架和AspectJ等AOP框架中,JoinPoint 是一个重要的上下文对象,用于获取和操作连接点的信息。

JoinPoint 提供了以下信息和功能:

  1. 连接点的方法信息:你可以通过 JoinPoint 获取正在执行的方法的信息,包括方法名、参数、返回类型等。

  2. 目标对象信息:你可以获得目标对象(被代理的对象)的引用。

  3. 连接点的静态部分:连接点是程序执行过程中的一个具体时刻,它包括一个静态部分(例如方法签名)和一个动态部分(例如方法参数的值)。

  4. 对连接点进行干预:在环绕通知中,你可以使用 ProceedingJoinPoint 对象来控制连接点的执行,包括是否执行连接点,是否修改连接点的参数或返回值等。

在Spring框架中,当你创建切面并使用注解 @Before@After@Around 等时,你可以将 JoinPoint 作为方法参数来访问连接点的信息。例如:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAspect {

    @Before("execution(* com.example.MyService.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        // 访问连接点的方法信息
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Before executing method: " + methodName);

        // 访问目标对象信息
        Object targetObject = joinPoint.getTarget();
        System.out.println("Target object: " + targetObject.getClass().getName());
    }
}

在上述示例中,JoinPoint 被用于获取连接点的方法名和目标对象信息,并在前置通知中输出这些信息。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

日志例子

当谈论AOP(Aspect-Oriented Programming,面向切面编程),可以使用一个通俗易懂的例子来解释它:

例子:日志记录

假设你正在开发一个电子商务网站,你希望记录每次用户购买商品的操作以进行日志记录。你可以使用AOP来实现这个功能。

传统方法是在每个购买操作的方法中手动插入日志记录的代码,例如:

public class ShoppingCartService {
    public void purchaseItem(User user, Product product) {
        // 执行购买操作
        // 记录日志
        Logger.log("User " + user.getUsername() + " purchased " + product.getName());
    }
}

但这样做会导致以下问题:

  1. 污染业务逻辑:日志记录代码与购买操作混在一起,使代码难以阅读和维护。

  2. 重复代码:如果你的应用有多个地方需要记录日志,你需要在每个地方复制相同的日志记录代码。

使用AOP,你可以将日志记录操作抽象为一个切面(Aspect),并将其应用到需要的地方,而不必在每个方法中手动插入日志记录代码。

首先,你创建一个日志切面:

@Aspect
public class LoggingAspect {
    @Before("execution(* com.example..*Service.purchase*(..)) && args(user, product)")
    public void logPurchase(User user, Product product) {
        Logger.log("User " + user.getUsername() + " purchased " + product.getName());
    }
}

然后,在需要记录日志的地方,你只需使用 @LogPurchase 注解标记:

@Service
public class ShoppingCartService {
    @LogPurchase
    public void purchaseItem(User user, Product product) {
        // 执行购买操作
    }
}

这样,你将日志记录的关注点从业务逻辑中分离出来,代码更加清晰,可维护性更高。而且,如果你想要在其他地方记录购买操作的日志,只需使用 @LogPurchase 注解即可,不必复制日志记录代码。

总之,AOP允许你通过将横切关注点(如日志记录、安全性、事务管理等)与主要业务逻辑分离,以实现更清晰、可维护和可重用的代码。这个示例中的日志记录就是AOP的一个典型应用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值