• 【Spring】aop的底层原理


    🎄欢迎来到@边境矢梦°的csdn博文🎄

     🎄本文主要梳理 Spring 中的切面编程aop的底层原理和重点注意的地方 🎄
    🌈我是边境矢梦°,一个正在为秋招算法竞赛做准备的学生🌈
    🎆喜欢的朋友可以关注一下🫰🫰🫰,下次更新不迷路🎆

    Ps: 月亮越亮说明知识点越重要 (重要性或者难度越大)🌑🌒🌓🌔🌕  

     

    目录

    ❤️切面编程介绍

    🌸连接点和切入点的区别🚀

    🌈Spring中扫描包注册bean

    ✨Spring中切面编程的两种实现

    🎈切入表达式


    ❤️切面编程介绍

    🎈Spring的切面编程是通过AOP(面向切面编程)机制来实现。AOP是一种编程范式,旨在将横切关注点(如日志记录、事务管理等)与核心业务逻辑分离以提高代码的模块性、可重用性和可维护性。

    在Spring中,切面由两部分组成:切点(Pointcut)和通知(Advice)。

    📌切点定义了在哪些连接点上应用切面逻辑。连接点是程序执行过程中可以插入切面逻辑的特定点,例如方法的执行、方法的调用等。切点可以使用表达式来选择特定的连接点,可以根据方法名、类名、注解等条件进行选择。

    📌通知是切面逻辑的具体实现。它定义了在切点上要执行的行为,可以在切点的前后、异常抛出时、方法返回时等时机执行。常见的通知类型包括前置通知(Before)、后置通知(After)、异常通知(AfterThrowing)和返回通知(AfterReturning)等。

    ✍️除了切点和通知,切面还可以包括引入(Introduction)和切面顺序(Aspect Ordering)等功能。引入允许在现有类中添加新的方法和属性以增强类的功能。切面顺序定义了多个切面的执行顺序,以控制切面逻辑的执行顺序。

    在Spring中,切面可以通过XML配置基于注解的配置或者基于Java的配置来定义。Spring提供了多种AOP实现,✨包括基于JDK的动态代理和基于CGlib的字节码生成。

    🎉通过使用Spring的切面编程,可以将共同的横切关注点从核心业务逻辑中剥离出来,以提高代码的可维护性和可重用性。切面编程在日志记录、事务管理、安全性控制等方面有广泛的应用。


    🌸连接点和切入点的区别🚀

    在Spring框架的AOP编程中,连接点(Join Point)和切入点(Pointcut)是两个相关但不同的概念。

    📌连接点是在程序执行过程中,可以插入切面逻辑的特定点。这些点可以是方法的执行、方法的调用或者异常的抛出等。在Spring中,连接点通常是方法的执行。连接是bean中的一些点

    📌切入点是指在连接点中选择特定的连接点。它是一个表达式,描述了哪些连接点将被切面逻辑所应用。切入点可以根据方法名、类名、注解等条件来选择特定的连接点。

    换句话说,切入点是一个定义,它决定了在哪些连接点上切面逻辑将会被执行。连接点是实际的程序执行点(Bean的),而切入点是定义了在哪些连接点上应用切面逻辑的规则。

    🌰例如,可以通过切入点表达式来选择在所有的Service接口的方法执行时应用切面逻辑。而连接点则是具体的某个Service接口的方法执行。

    在Spring AOP中,切入点表达式是使用AspectJ切入点表达式语言编写的,它具有灵活的语法,可以根据需求选择不同的连接点。(下文中)


    🌈Spring中扫描包注册bean

    🥝切面类不需要扫描进bean

    在Spring中,当你使用 进行包扫描时,它默认会扫描指定包及其子包中的类,并将带有特定注解的类注册为Spring容器中的bean。如果类没有加上注解,它将不会被注册为bean,也就不会被Spring进行管理。

    然而,与AOP(面向切面编程)有关的切面通常不需要被注册为bean,因为它们是通过特定的AOP配置来创建和管理的。切面通常是带有 @Aspect 注解的类,并且它们的方法可以使用 @Before@After 等注解来定义切面逻辑。

    所以,如果你的切面类(带有 @Aspect 注解的类)在指定的包中,它会被扫描到,但不会被注册为Spring容器中的bean。相反,Spring会使用AOP配置来创建并管理这些切面,例如使用 @EnableAspectJAutoProxy 这样的配置来启用自动代理,从而将切面应用到相应的bean中。

    总之,不需要将切面类注册为Spring bean,Spring会自动处理AOP相关的配置和代理生成。只需确保切面类在扫描的包中以及AOP配置正确即可。

    🥝切面应用到的目标bean需要被扫描进bean

    切面应用到的目标bean通常需要被Spring加载进容器中,以便Spring可以管理这些bean并应用切面的横切关注点(cross-cutting concerns)。

    🏀总结切面的流程

    具体来说,当你使用Spring AOP来应用切面时,以下是通常的流程:

    1. 你定义了一个切面类,该类使用 @Aspect 注解进行标记,同时定义了切面逻辑,如@Before、@After等通知。

    2. 你还定义了一个或多个目标bean,这些bean是你的应用程序的组成部分,切面将会应用到这些bean的方法上。

    3. Spring容器会扫描并加载这些目标bean,将它们实例化并管理它们的生命周期。

    4. 你配置Spring AOP,告诉Spring在哪些切点(方法执行点)应用你的切面。这通常通过@EnableAspectJAutoProxy 这样的配置来完成。

    5. Spring会自动为目标bean创建代理对象,这些代理对象包含了切面逻辑。当你调用目标bean的方法时,切面逻辑会在方法执行前后生效,从而实现横切关注点的功能。

    所以,虽然切面类本身不需要被显式注册为Spring bean,但目标bean需要被Spring加载并纳入容器管理,以便切面可以应用到它们的方法上。这就是Spring AOP的工作原理。


    ✨Spring中切面编程的两种实现

    在Spring AOP中,不仅可以对实现了接口的bean进行切面编程,还可以对没有实现接口的bean进行切面编程。Spring使用代理对象来实现AOP,而代理对象可以基于接口(JDK动态代理)基于类(CGLIB代理)创建因此不需要目标bean实现接口也可以应用切面。

    具体取决于AOP代理的方式:

    1. JDK动态代理:当目标bean实现了接口时,Spring会使用JDK动态代理来创建代理对象。这意味着只有实现了接口的方法才能被切面所影响。

    2. CGLIB代理:当目标bean没有实现接口时,Spring会使用CGLIB代理来创建代理对象。CGLIB可以代理没有实现接口的类,因此可以应用到这些类的方法。

    ✍️两个动态代理的区别

    1. JDK动态代理是面向接口的,只能增强实现类中接口中存在的方法。CGlib是面向父类的,可以增强父类的所有方法
    2. JDK得到的对象是JDK代理对象实例,而CGlib得到的对象是被代理对象的子类

    📌要注意的是,如果你使用基于注解的切面编程(例如使用 @Aspect 注解),Spring AOP会自动选择适当的代理方式,无需手动指定只需确保你的切面和目标bean都配置正确,Spring会处理代理的创建和切面的应用。

    所以,Spring AOP可以用于实现对实现接口和未实现接口的bean的切面编程。选择代理方式取决于目标bean是否实现了接口。

    🎈切入表达式

    📌图来自 : (III)AOP:第四节:切入点表达式 - 格物致知_Tony - 博客园 (cnblogs.com)

     切入点表达式

    🥝作用
    通过表达式的方式定位一个或多个具体的连接点。
    🌸🌈语法细节
    ①切入点表达式的语法格式

    execution([权限修饰符][返回值类型][简单类名/全类名][方法名]([参数列表]))

    ②🌰 基本使用

    表达式execution(* com.sina.spring.ArithmeticCalculator.*(..))
    含义

    ArithmeticCalculator接口中声明的所有方法。

    第一个“*”代表任意修饰符及任意返回值。

    第二个“*”代表任意方法。
    “..”匹配任意数量、任意类型的参数。
    若目标类、接口与该切面类在同一个包中可以省略包名。

    表达式execution(public * ArithmeticCalculator.*(..))
    含义ArithmeticCalculator接口的所有公有方法
    表达式execution(public double ArithmeticCalculator.*(..))
    含义ArithmeticCalculator接口中返回double类型数值的方法
    表达式execution(public double ArithmeticCalculator.*(double,..))
    含义

    第一个参数为double类型的方法。

    “..”匹配任意数量、任意类型的参数。

    表达式execution(public double ArithmeticCalculator.*(double, double))
    含义参数类型为double,double类型的方法
    表达式execution(* *.add(int,….)) l execution(* *.sub(int,..))
    含义任意类中第一个参数为int类型的add方法或sub方法

  • 相关阅读:
    虚拟机中centos扩展根目录空间
    JS实现对用户名、密码进行正则表达式判断,网页跳转
    【原创】EtherCAT主站IgH解析(二)-- 如何将Igh移植到Linux/Windows/RTOS等操作系统指南
    【C++】C++智能指针
    对“Linux中遇到的RAID阵列”好好总结一次
    adb shell pm path packageName
    css宽高自适应
    Scala入门教程
    腾讯专家献上技术干货,带你一览腾讯广告召回系统的演进
    RadSegNet: A Reliable Approach to Radar Camera Fusion 论文笔记
  • 原文地址:https://blog.csdn.net/dandelionl_/article/details/132752598