来自云龙湖轮廓分明的月亮 发表于 2024-6-14 21:06:13

Spring 源码:深度解析AOP源码配置解析

https://img-blog.csdnimg.cn/direct/53cc1ffca81a4f5aadb228002e169472.gif#pic_center


一、 解析AOP配置的入口

1.1 从XML配置到AOP Namespace的解析流程

流程解析:

[*]加载配置文件:Spring 应用启动时加载 XML 配置文件。
[*]解析 XML 配置:

[*]Spring 解析器会辨认 AOP 相干的 XML 元素, 如 <aop:config>、<aop:aspect>、<aop:pointcut>、<aop:before> 等 。
[*]对于利用 AOP 命名空间的配置,Spring 会根据命名空间中定义的 schema 头文件来解析配置。

[*]创建切面和通知:

[*]Spring 解析到 <aop:aspect> 元素时,会创建对应的切面,并指定切面的 ID 和引用的 bean。
[*]解析<aop:before>、<aop:after> 等元素时,会创建对应的通知,并指定通知要执行的方法。

[*]解析切点:

[*]当解析到 <aop:pointcut>元素时,Spring 会创建一个切点,并指定切点的 ID 和表达式。

[*]应用切面和通知:

[*]Spring 将切面、切点和通知组合在一起,根据配置中的切点表达式找到目标方法。
[*]然后,Spring 根据配置的通知范例(如前置通知、后置通知等),将通知织入到目标方法的执行流程中。

[*]创建署理对象:

[*]如果目标类被署理,Spring 将根据配置创建署理对象。
[*]对于基于接口的署理,Spring 利用 JDK 动态署理。
[*]对于基于类的署理,Spring 利用 CGLIB 动态署理。

[*]运行时织入:

[*]当应用程序运行时调用目标方法时,署理对象会按照配置织入相应的通知,实现切面功能。

[*]执行目标方法:

[*]最后,Spring 框架会执行被署理的目标方法,并在执行过程中触发配置的通知。

1.2 分析注解驱动的AOP配置解析流程

解析流程:

[*]扫描组件:

[*]Spring 应用启动时,会扫描指定的包路径下的组件,并解析其中的注解。

[*]解析切面:

[*]Spring 容器会检测到被 @Aspect 注解标记的类,并将其辨认为切面类。
[*]在切面类中,Spring 解析带有 @Before、@After、@Around 等注解的方法,这些注解表示切面的通知范例。

[*]辨认切点:

[*]切面类中的方法可能带有 @Pointcut 注解,用于定义切点表达式,Spring 会解析这些表达式并创建切点。

[*]应用通知:

[*]Spring 解析切面类中带有 @Before、@After、@Around 等注解的方法,并将其作为通知,与对应的切点关联。

[*]创建署理对象:

[*]对于被署理的目标类,Spring 根据配置情况选择利用 JDK 动态署理还是 CGLIB 动态署理。
[*]如果目标类实现了接口且配置了基于接口的署理,则利用 JDK 动态署理;否则,利用 CGLIB 动态署理。

[*]运行时织入:

[*]当应用程序调用被署理的目标方法时,Spring 框架会根据切面和通知的配置,在方法执行前后织入相应的通知。

[*]执行目标方法:

[*]最终,Spring 框架会执行被署理的目标方法,并在执行过程中触发配置的通知,完成 AOP 的功能。

二、AOP配置解析的核心流程

2.1 ConfigBeanDefinitionParser 类

   ConfigBeanDefinitionParser 类是 AOP 配置的 Bean 定义解析器。负责解析 <aop:config> 标签中的配置信息,并将解析结果应用到 Spring 的 Bean 定义中。
主要责任:

[*]解析 AOP 配置信息:解析<aop:config> 标签及其子标签中的配置信息,包罗切面定义、通知范例、切点表达式等。
[*]创建切面相干的 BeanDefinition:根据解析的配置信息,创建切面相干的 BeanDefinition 对象,包罗切面、通知等。
[*]注册 BeanDefinition:将解析得到的 BeanDefinition 注册到 Spring 的 BeanFactory 中,使得这些切面相干的组件可以被容器管理。
[*]处置惩罚切面相干的后处置惩罚逻辑:在注册切面相干的 BeanDefinition 之前或之后,可能需要进行一些额外的后处置惩罚逻辑,如检查和修正配置、添加其他配置等。
2.2 parse()

根据 <aop:config> 元素及其子元素的配置信息,进行相应的解析和处置惩罚,最终将 AOP 相干的配置信息转换为 Spring 容器内部的数据布局。
https://img-blog.csdnimg.cn/direct/ab8e56a7681441e092d5db563ba23b02.png#pic_center
2.3 parseAdvisor()

解析<advisor>元素及其子元素的配置信息,并根据解析结果注册相应的 BeanDefinition 到 Spring 容器中。
https://img-blog.csdnimg.cn/direct/90719e5dff7a4aa78a84a00062d0ca46.png#pic_center
2.4 parseAspect()

负责解析 <aspect> 元素及其子元素的配置信息,并根据解析结果注册相应的 BeanDefinition 到 Spring 容器中。
https://img-blog.csdnimg.cn/direct/07691b78175847b2896d9b9f47da914c.png#pic_center
2.5 parsePointcut()

解析切点元素,获取id和expression属性的值,并根据这些值创建和注册切点定义对象。
https://img-blog.csdnimg.cn/direct/572752bde3744c9aa9046ec4a5f3d174.png#pic_center
2.6 createAdvisorBeanDefinition()

根据传入的参数创建一个切面通知 Bean 定义对象,并设置相应的属性和构造器参数。
https://img-blog.csdnimg.cn/direct/606220a97c9c45788894183a1be5ee65.png#pic_center
2.7 createPointcutDefinition()

创建一个切点定义的 Bean,并设置其作用域、合成标记和表达式属性值,然后返回该 Bean 定义对象。
https://img-blog.csdnimg.cn/direct/d1d1717e486742af9d51ef179fc8bb63.png#pic_center
三、设计模式

3.1 JDK 动态署理


[*]署理模式:JDK 动态署理是典范的署理模式的应用。

[*]在署理模式中,署理对象充当了客户端和真实对象之间的中介,控制对真实对象的访问。
[*]JDK 动态署理中的署理对象就扮演了这样的角色,它通过实现目标对象相同的接口,并持有目标对象的引用,在调用方法时将请求转发给目标对象。
[*]署理模式的利用可以实现对目标对象的访问控制、延迟加载等功能。

[*]装饰器模式:在 JDK 动态署理中,InvocationHandler 接口扮演了类似于装饰器模式中的装饰器的角色。

[*]InvocationHandler 接口包含了对方法的调用处置惩罚逻辑,类似于装饰器模式中的装饰器对对象进行额外的包装和处置惩罚。
[*]通过 InvocationHandler 的实现类,可以在目标对象的方法调用前后参加额外的逻辑,从而实现类似于装饰器模式的功能。

[*]工厂模式:JDK 动态署理中的 Proxy 类通过 newProxyInstance 方法动态创建署理对象。

[*]newProxyInstance 方法可以看作是一个工厂方法,根据传入的类加载器、接口数组和 InvocationHandler 对象动态产生署理对象。

[*]反射模式:JDK 动态署理的实现基于 Java 的反射机制。

[*]通过反射机制可以在运行时获取并利用类、对象、接口等信息。
[*]署理对象在接收到方法调用时,利用反射机制将调用转发给 InvocationHandler 中的 invoke 方法进行处置惩罚,从而实现署理的功能。

3.2 CGLIB 署理


[*]委托模式:CGLIB署理通过生成目标类的子类来实现署理。

[*]在子类中重写目标方法并调用署理逻辑。
[*]这种方式类似于委托模式,即将目标对象的功能委托给署理对象来实现。

[*]模板方法模式:CGLIB生成的署理类通常利用了模板方法模式。

[*]在生成的子类中定义模板方法,并在模板方法中调用用户定义的回调方法(如署理逻辑)。
[*]这样设计使得用户能够通过继续署理类并重写回调方法来定义自己的署理逻辑。

[*]工厂模式:CGLIB署理通常涉及到署理类的创建过程,可看作是工厂模式的应用。

[*]CGLIB通过字节码生成技术在运行时动态生成署理类,为客户端提供了一种动态创建署理对象的方式,符合工厂模式的特点。

[*]计谋模式:CGLIB署理答应用户通过定义回调方法来实现署理逻辑,这样的设计类似于计谋模式的应用。

[*]用户可以根据需要定义不同的署理计谋(即不同的回调方法),并将其传递给CGLIB来生成相应的署理类。

[*]反射模式:CGLIB的实现基于对类的字节码进行利用,这样的设计类似于反射模式的应用。

[*]CGLIB利用了反射来生成署理类的字节码,并在运行时加载和处置惩罚这些字节码,从而实现署理功能。

3.3 AOP 面向切面


[*]署理模式:AOP 中的署理对象充当了目标对象和横切逻辑之间的中介,控制对目标对象方法的访问。

[*]通过署理模式,AOP实现了横切逻辑的注入,并在目标方法执行前后执行额外的逻辑,如日志纪录、性能监控等。

[*]装饰器模式:AOP 中的横切逻辑类似于装饰器模式中的装饰器。

[*]在目标方法的执行前后参加额外的逻辑。
[*]AOP框架在运行时动态地将这些横切逻辑织入到目标对象的方法调用中,类似于装饰器模式中的装饰器对对象进行包装和处置惩罚。

[*]观察者模式:AOP中的切面可以明确为观察者,观察目标对象方法的执行,并在特定的切点上执行相应的逻辑。

[*]切面可以订阅特定的切点,当这些切点被触发时,切面就会执行相应的逻辑,类似于观察者模式中的观察者对目标对象的变革做出反应。

[*]工厂模式:AOP框架通常利用了工厂模式来创建署理对象。

[*]通过配置文件或注解等方式定义切面和切点,AOP框架会根据这些定义动态地创建署理对象,并将横切逻辑织入到目标对象的方法调用中,从而实现面向切面编程的功能。

[*]模板模式:AOP框架中的署理对象通常利用了模板模式。

[*]AOP框架提供了一种模板化的方式来定义横切逻辑,并在执行目标方法前后调用相应的模板方法,这样的设计使得用户能够通过继续并重写模板方法来定义自己的横切逻辑。

四、实际与应用

   如安在实际项目中应用 Spring AOP 实现事务管理
假设有一个 简单的订单管理体系,包含订单服务和相干的实体类。盼望在创建订单的过程中实现事务管理,即要么全部乐成,要么全部失败。

[*]添加依靠。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 其他依赖 -->

[*]定义订单实体类 Order 。
/**
* @Entity: 这个注解表明这是一个 JPA 实体类
*
*/
@Entity
public class Order {
    /**
   * @Id: 表示该字段是实体类的主键
   * @GeneratedValue: 指定了主键的生成策略 -> GenerationType.IDENTITY:主键值会自动增加
   */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String orderNumber;

    private double amount;

    // Getters and setters
}

[*]定义订单服务类 OrderService,并添加创建订单的方法。
/**
* 在方法上添加 @Transactional 注解,Spring AOP 可以在方法执行前后自动管理事务的开启、提交和回滚
*/
@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    public void createOrder(String orderNumber, double amount) {
      Order order = new Order();
      order.setOrderNumber(orderNumber);
      order.setAmount(amount);
      orderRepository.save(order);
    }
}

[*]配置事务管理:在 Spring Boot 主类上添加 @EnableTransactionManagement 注解,启用事务管理。
@SpringBootApplication
@EnableTransactionManagement
public class MyApplication {

    public static void main(String[] args) {
      SpringApplication.run(MyApplication.class, args);
    }
}
攀登顶峰,这种奋斗的自己就足以充实人的心

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Spring 源码:深度解析AOP源码配置解析