SpringBoot整合Aop源码之Aop的加载过程分析

本文介绍了AOP的概念,SpringBoot如何利用AOP提高代码模块化,详细剖析了AOP的关键概念如切面、切点、通知和连接点,以及在SpringBoot项目中AOP的自动配置过程,重点讲解了`proxyTargetClass`的选择和其对代理机制的影响。
摘要由CSDN通过智能技术生成

Spring Boot AOP 简介

什么是AOP?

AOP(面向切面编程)是一种软件开发范式,旨在通过在程序中插入称为“切面”的特殊代码,来提高代码的可维护性、可重用性和可扩展性。切面是与业务逻辑无关的代码,用于横切关注点,例如日志记录、性能监测、事务管理等。

在传统的面向对象编程中,应用程序的逻辑通常被分解为多个对象,这些对象协同工作以完成任务。而AOP允许你通过横切关注点的方式来封装这些协同工作,从而提高代码的模块化程度。

AOP中的关键概念

切面(Aspect)

切面是一个模块化单元,其中包含有关横切关注点的信息。它包含了切点、通知和一些其他配置。

切点(Pointcut)

切点是一个表达式,它定义了在哪里以及何时应该应用切面。它指定了在程序中的哪些位置切入,例如在方法执行之前或之后。

通知(Advice)

通知是在切入点上执行的代码。Spring框架定义了几种类型的通知,包括前置通知(在方法执行前执行)、后置通知(在方法执行后执行)、异常通知(在方法抛出异常时执行)、环绕通知(在方法执行前后都执行)等。

连接点(Join Point)

连接点是在应用程序执行期间能够插入切面的点。通常,连接点是方法的执行。

源码分析

我们要在SpringBoot项目中使用AOP功能,需要在我们的Pom.xml文件中引入以下依赖

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

那么会间接引入如下依赖的jar包

从截图中可以看到又间接依赖了Aspectj的jar包,那么我们的项目的Classpath中就存在了Advice这个类,这个点很关键一会会具体说到这个类带来的作用。

在SpringBoot的spring-boot-autoconfigurejar包中有一个自动配置类,它是org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,它的部分代码截图如下:

可以看出里面有个自动配置类是AspectJAutoProxyingConfiguration,它的条件注解是@ConditionalOnClass(Advice.class),即我们的ClassPath下面存在Advice这个类的时候才会被加载,前面已经说过了我们这个类路径下正好存在Advice这个类,那么就会加载AspectJAutoProxyingConfiguration,那么下面的静态自动配置类也会加载会解析CglibAutoProxyConfiguration这个配置类,进而解析上面的注解标签@EnableAspectJAutoProxy(proxyTargetClass = true),这个注解里面是什么内容那?先看看代码


package org.springframework.context.annotation;

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

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {


  //proxyTargetClass 用于决定是使用基于接口的代理还是基于类的代理,默认为 false 表示使用基于接口的代理。
   boolean proxyTargetClass() default false;

  //exposeProxy 用于在切面内部暴露代理对象,以便切面可以访问代理对象,通常用于解决循环依赖的问题。
   boolean exposeProxy() default false;

}

这里面又引入了一个AspectJAutoProxyRegistrar对象,该对象实现了ImportBeanDefinitionRegistrar接口,我们重点看registerBeanDefinitions方法的定义:

public void registerBeanDefinitions(
      AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

   AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

   AnnotationAttributes enableAspectJAutoProxy =
         AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
   if (enableAspectJAutoProxy != null) {
      if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
         AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
      }
      if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
         AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
      }
   }
}

首先调用AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,沿着方法的执行会执行到registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,该方法的代码如下:

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

可以看到这个方法又调用了registerOrEscalateApcAsRequired方法,传入了一个AnnotationAwareAspectJAutoProxyCreator类型的Class类,这个方法代码如下:

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
        BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }

        return null;
    } else {
        RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
        beanDefinition.setSource(source);
        beanDefinition.getPropertyValues().add("order", -2147483648);
        beanDefinition.setRole(2);
        registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
        return beanDefinition;
    }
}

判断容器注册类中是否包含一个Bean名称是org.springframework.aop.config.internalAutoProxyCreator的Bean定义,很显然第一次指定是不包含的,会走到else方法,创建一个类型是AnnotationAwareAspectJAutoProxyCreator的Bean定义对象RootBeanDefinition,添加一个order的属性,并且设置为最高优先级,然后把这个Bean定义对象放到容器注册中,bean的名字是org.springframework.aop.config.internalAutoProxyCreator。

回到registerBeanDefinitions方法的执行,这次判断注解EnableAspectJAutoProxy上的属性值的内容,判断proxyTargetClass的属性是否为true,如果为true说明是cglib代理,本例子中的确是用的cglib代理,然后调用AopConfigUtils.forceAutoProxyCreatorToUseClassProxying方法,该方法的代码如下:

public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
   if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
      BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
      definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
   }
}

该方法判断是否有org.springframework.aop.config.internalAutoProxyCreator的Bean定义,由于上面的方法已经注册进去了,所以会走到if语句中,又往Bean的定义中添加了一个proxyTargetClass的属性,值为true。

好了,到这为止Aop的加载过程分析就结束了,比较简单,下面我做一个总结。

总结

本节阐述了Aop的概念以及加载AOP的源码分析,总的来说就是我们项目中如果需要使用Aop的功能需要引入maven的依赖,spring-boot-starter-aop,然后项目自动会引入AspectJ的jar包依赖,由于springboot自动配置类AopAutoConfiguration中判断类路径下是否有Advice这个类,如果有会加载自动配置类CglibAutoProxyConfiguration进而又导入了AspectJAutoProxyRegistrar对象然后往spring容器中添加了一个Bean名称是org.springframework.aop.config.internalAutoProxyCreator,类型是AnnotationAwareAspectJAutoProxyCreator的类,下一篇文章我会继续分析AnnotationAwareAspectJAutoProxyCreator是什么以及调用过程都发生了什么事情。

好了,我是程序员老徐,如果喜欢我的文章请点赞关注,我们下期见。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值