Spring AOP源码-代理生产过程

前言

在分析 Spring AOP 源码之前,如果你对 Spring IOC、依赖注入(DI) 原理不是很清楚,建议您先了解一下:Spring IOC 源码解析、Spring 依赖注入(DI) 源码解析,这样或许会让你的思路更加清晰。

一、AOP 名词介绍?

在AOP中,共涉及到**1.切面 Aspect****2.连接点 JoinPoint****3.通知 Advice****4.切入点 Pointcut****5.目标对象 Target Object**这 5 个名词。

二、AOP执行顺序

Spring5 AOP顺序

从Spring5.2.7开始,Spring AOP不再严格按照AspectJ定义的规则来执行advice,而是根据其类型按照从高到低的优先级进行执行:@Around,@Before ,@After,@AfterReturning,@AfterThrowing。
在这里插入图片描述

二、AOP 源码分析从何入手

1.AOP核心类介绍

介绍一些Spring Aop中一些核心类,大致分为三类:

在这里插入图片描述

(1)AbstractAutoProxyCreator:继承 spring ioc的扩展接口 BeanPostProcessor,主要用来扫描获取 advisor。BeanPostProcessor作用: Spring容器中完成bean实例化、配置以及其他初始化方法前后要添加一些自己逻辑处理。

AbstractAdvisorAutoProxyCreator:默认扫描所有Advisor的实现类。相对于根据Bean名称匹配,该类更加灵活。动态的匹配每一个类,判断是否可以被代理,并寻找合适的增强类,以及生成代理类。

AnnotationAwareAspectJAutoProxyCreator:目前最常用的AOP使用方式。spring aop 开启注解方式之后,该类会扫描所有@Aspect()注释的类,生成对应的advisor。目前SpringBoot框架中默认支持的方式,自动配置。

(2) Advisor:顾问的意思,封装了spring aop中的切点和通知。 就是我们常用的@Aspect 注解标记得类
(3) Advice:通知,也就是aop中增强的方法。

3.Spring启动中AOP大致流程

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {

		// Allows post-processing of the bean factory in context subclasses.
		postProcessBeanFactory(beanFactory);

		// AnnotationAwareAspectJAutoProxyCreator
		invokeBeanFactoryPostProcessors(beanFactory);

		// 获取@Aspect标记的类,生成Advicsor,加入缓存
		registerBeanPostProcessors(beanFactory);
		…………
		// 获取容器中所有的bean后置处理器集合遍历每个processor,将bean变成代理对象
		finishBeanFactoryInitialization(beanFactory);

		// Last step: publish corresponding event.
		finishRefresh();
	}
}

下面讲根据Spring启动流程,分成两个阶段讲解AOP源码,第一个阶段AOP配置解析,第二个阶段容器中单实例Bean(需AOP)的创建过程

三、AOP配置解析

使用注解版的AOP实现切面功能时,AOP功能如果要生效,必须先在配置类上使用注解开启AOP支持,使用到的注解就是**@EnableAspectJAutoProxy**,配置类如下:

@Configuration
@ComponentScan(basePackages = "com.wb.spring.aop")
// 开启AOP支持
@EnableAspectJAutoProxy
public class AopConfig {
}

一、AOP的前期初始化过程

1.1、后置处理器初始化过程分析

那么**@EnableAspectJAutoProxy**注解究竟干了什么事呢?打开其源码实现,如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 通过Import注解给容器中导入AspectJAutoProxyRegistrar组件.
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
   /**
    * 用来指定是否使用CGLIB的方式来创建代理
    * 默认值为false,表示使用基于接口的JDK动态代理方式来创建
    */
   boolean proxyTargetClass() default false;
   /**
    *用来设置是否将代理类暴露到AopContext中。如果将代理暴露在AopContext中,
    *代理类将会被保存在ThreadLocal中,在需要使用代理的时候直接从ThreadLocal中获取。
    */
   boolean exposeProxy() default false;
}

​ 通过源码可以发现,在**@EnableAspectJAutoProxy注解中,又使用了@Import注解给容器中导入了一个AspectJAutoProxyRegistrar**组件(关于@Import注解之前文章中已经详细介绍过,此处不再介绍),那么这个组件又是干什么的呢?继续打开其源码,如下:

// 实现了ImportBeanDefinitionRegistrar接口,用来在Spring启动时给bean注册中心自定义注册组件
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      // 如果有需要,则给容器中注册一个AspectJAnnotationAutoProxyCreator组件.
      AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
      // 获取EnableAspectJAutoProxy注解上标注的属性值
      AnnotationAttributes enableAspectJAutoProxy =
            AnnotationConfigUtils.attributesFor(importingClassMetadata, 
                    EnableAspectJAutoProxy.class);
      // ... 其他源码暂时省略
   }
}

这个组件是给容器的bean定义注册中心自定义注册一个bean组件,通过调用AOP工具类中AopConfigUtils中的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法完成。

​ 继续查看方法registerAspectJAnnotationAutoProxyCreatorIfNecessary的实现,内容总共分为如下的三步,最终的效果就是给容器中注册一个名称为internalAutoProxyCreator,类型为AnnotationAwareAspectJAutoProxyCreator的bean组件。

public abstract class AopConfigUtils {
  // 第一步:调用该方法给容器中注册一个bean组件
  @Nullable
  public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
     return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
  }
  // 第二步:再次调用该方法,传入bean组件的类型为:AnnotationAwareAspectJAutoProxyCreator的bean组件
  @Nullable
  public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        BeanDefinitionRegistry registry, @Nullable Object source) {
      return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
  }
  /**
   * 第二步:最后调用这个方法给容器中注册一个名称为org.springframework.aop.config.internalAutoProxyCreator,
   *   类型为AnnotationAwareAspectJAutoProxyCreator的组件
   */
  @Nullable
  private static BeanDefinition registerOrEscalateApcAsRequired(
      Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    // cls = AnnotationAwareAspectJAutoProxyCreator.class
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

    // 判断容器中是否有名称为org.springframework.aop.config.internalAutoProxyCreator的Bean定义,第一次运行的时候没有.
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
      BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
      if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
        int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
        int requiredPriority = findPriorityForClass(cls);
        if (currentPriority < requiredPriority) {
          apcDefinition.setBeanClassName(cls.getName());
        }
      }
      return null;
    }
    // 创建一个AnnotationAwareAspectJAutoProxyCreator类型的Bean定义
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    // 设置最高优先级.
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // 设置bean定义的名称为:‘org.springframework.aop.config.internalAutoProxyCreator’
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
  }
  // ... 工具类中的其他代码暂时省略.
}

所以,重点就转移到了这个类型为AnnotationAwareAspectJAutoProxyCreator(上面所述AOP核心类)的bean组件,这个组件的功能及运行时机搞清楚,整个AOP的原理就清楚了,后续的其他类似使用注解@Enablexxx实现的功能,例如注解版事务@EnableTransactionManagement,套路都是一样,都是给容器中注册一些组件,然后在某个时机执行对应的方法。深究其底层原理其实通过查看该注解给容器中注入的组件就可以了。

AnnotationAwareAspectJAutoProxyCreator这个类最终是实现了BeanPostProcessorAware接口的,Spring的一个最强大的功能就是可以支持灵活扩展,提供了很多扩展点,这两个接口就是Spring中提供的两个重要扩展点,而且这两个扩展点对应的方法会在Bean的创建过程中被调用。所以,重点再一次转移到和这些后置处理器相关的方法上,查看后置处理器如何完成调用的。

1.2、后置处理器初始化的调用过程

(1)在refresh方法中调用registerBeanPostProcessors方法给容器中注册后置处理器,这个过程中,就会注册与Aop相关的后置处理器AnnotationAwareAspectJAutoProxyCreator

(2)在注册AnnotationAwareAspectJAutoProxyCreator的过程中,会通过Spring的Bean创建过程去完成后置处理器Bean组件的实例化,属性赋值及初始化的操作。实例化即通过反射的方式去创建对象;填充属性,即通过内省操作对象的setter方法完成;初始化操作中,又分为如下几个小步骤:

invokeAwareMethods(beanName, bean),主要是用来调用与Aware接口有关的方法,因为这个AOP对应的后置处理器实现了BeanFactoryAware接口,所以会调用其setBeanFactory方法;

applyBeanPostProcessorsBeforeInitialization,在bean初始化之前调用,可以再bean创建之前做一些自定义的操作,因为AOP对应的后置处理器实现了BeanPostProcessor接口,所以会调用该方法;

//获取Adivsor
public List<Advisor> buildAspectJAdvisors() {
		List<String> aspectNames = this.aspectBeanNames;	
		if (aspectNames == null) {
		synchronized (this) {
			aspectNames = this.aspectBeanNames;
			if (aspectNames == null) {
				List<Advisor> advisors = new ArrayList<>();
				aspectNames = new ArrayList<>();
        //1、获取到容器中所有对象的beanNames集合(非常耗时)
        //2、遍历每个beanName
        //3、获取beanName对应类对象
				String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
						this.beanFactory, Object.class, true, false);
				for (String beanName : beanNames) {
					if (!isEligibleBean(beanName)) {
						continue;
					}
					Class<?> beanType = this.beanFactory.getType(beanName);
					if (beanType == null) {
						continue;
					}
					if (this.advisorFactory.isAspect(beanType)) {
            //① 收集beanName
						aspectNames.add(beanName);
						AspectMetadata amd = new AspectMetadata(beanType, beanName);
						if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
              //② 构建工厂对象
							MetadataAwareAspectInstanceFactory factory =
									new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
              //③ 生成增强器
							List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
              //④ 加入缓存
							if (this.beanFactory.isSingleton(beanName)) {
								this.advisorsCache.put(beanName, classAdvisors);
							}
							else {
								this.aspectFactoryCache.put(beanName, factory);
							}
							advisors.addAll(classAdvisors);
						}
						else {
							//省略
						}
					}
				}
				this.aspectBeanNames = aspectNames;
				return advisors;
			}
		}
	}
	//省略
	return advisors;
}

invokeInitMethods,执行自定义的初始化方法,例如:init-method指定的方法或者如果bean实现了InitializingBean接口,则在该步骤中会执行afterPropertiesSet方法;

applyBeanPostProcessorAfterInitialization,在bean初始化之后调用,可以再bean初始化之后,再做一些自定义操作,由于AOP对应的后置处理器实现了BeanPostProcessor接口,所以该方法在Bean初始化完成之后也会被调用,Spring创建AOP的代理对象就是在该步骤中完成的,通常有两种方式:cglib和JDK动态代理;

(3)经过上述一系列操作之后,AnnotationAwareAspectJAutoProxyCreator后置处理器就被创建完成,然后将创建完成的后置处理器组件加入到Spring容器中的beanFactory对象中。

上述整个过程调用链比较深,代码也比较多。篇幅有限,就不贴代码了,在看源码的过程中,可以根据上述的过程去一步步查看。

1.3、容器中单实例Bean的创建过程

​ 上述过程完成之后,只会给容器中注册一个AnnotationAwareAspectJAutoProxyCreator类型的后置处理器。但是容器中需要被增强的Bean示例还未创建出来,那么这个Bean示例是在什么时候被创建的呢?下面分析一下其创建过程。

(1)在容器刷新refresh过程的倒数第二步中会完成容器中剩余的单实例Bean的创建及初始化。即:**finishBeanFactoryInitialization(beanFactory)**方法。

(2)而在创建bean的过程中,会受到容器中后置处理器的拦截操作,包括上述第一步给容器中注册的AnnotationAwareAspectJAutoProxyCreator后置处理器,会在Bean创建前后及初始化前后执行后置处理器方法,对Bean做一些自定义的拦截操作,包括对bean进行包装,生成对应的代理对象;

进入AbstractAutoProxyCreator(抽象类)

  1. 容器中查找 Advisor类型(事务)

  2. 容器中查找注解了@aspectj的bean(重点)

(3)在创建TestController(查看附录)对应的bean时,被后置处理器拦截到之后,会执行如下的处理过程,具体创建代理对象的过程在postProcessAfterInitialization方法中:

① 判断当前bean是否已经被增强过,如果已经被增强,则会直接返回这个bean;判断依据就是根据bean的名称判断已经增强的bean对应的Map集合中是否包括当前需要创建的bean;

② 如果当前bean未被增强过,则去判断当前bean是否需要被包装,如果不需要被包装,则直接返回原来的bean,如果需要被包装,则会通过AbstractAutoProxyCreatorwrapIfNecessary方法进行包装;

③ 如果需要被包装,即执行了wrapIfNecessary方法,则会先去获取所有的增强,如果能够获取到增强器,则会调用AbstractAutoProxyCreator的createProxy方法去创建代理对象。

AbstractAutoProxyCreator.createProxy方法的创建逻辑:

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
      @Nullable Object[] specificInterceptors, TargetSource targetSource) {
   ProxyFactory proxyFactory = new ProxyFactory();
   proxyFactory.copyFrom(this);
   // 其他不相关的代码略...
   
   // 默认不指定生成代理的方式,则proxyTargetClass为false,使用的是jdk动态代理
   // 如果未强制指定使用cglib生成动态代理,则会去校验当前接口是否能够正常生成代理对象
   if (!proxyFactory.isProxyTargetClass()) {
      // 当前bean定义中指定了preserveTargetClass属性为true,则会强制使用cglib动态代理
      if (shouldProxyTargetClass(beanClass, beanName)) {
         proxyFactory.setProxyTargetClass(true);
      }
      else {
         // 判断当前bean实现的接口是否为标记类的接口
         //   如果为标记类的接口,例如:Aware接口,则还是会强制使用cglib去生成代理
         evaluateProxyInterfaces(beanClass, proxyFactory);
      }
   }
   /** 找到所有的advisors增强点,即:环绕,前置,后置,返回,异常等通知方法 */
   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
   /** 设置增强点 */
   proxyFactory.addAdvisors(advisors);
   /** 设置需要代理的目标类 */
   proxyFactory.setTargetSource(targetSource);
   /** 定制代理工厂,可以使用自定义的代理工厂,此处使用的还是默认的DefaultAopProxyFactory工厂 */
   customizeProxyFactory(proxyFactory);
   proxyFactory.setFrozen(this.freezeProxy);
   if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
   }
   /** 创建代理对象,里面会判断使用JDK动态代理还是CGLIB动态代理 */
   return proxyFactory.getProxy(getProxyClassLoader());
}

创建代理的时候会根据条件去决定是使用cglib创建代理还是根据jdk去创建代理,如下DefaultAopProxyFactory的createAopProxy方法,该方法源码如下:

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
   // isOptimize:表示是否需要对生成代理的策略进行优化,可以在配置中指定
   // isProxyTargetClass:表示是否需要强制使用cglib来生成代理,默认为false,通常都会指定强制使用cglib,即将该值设置为true
   // (当前被代理的类是否未实现接口,实现的接口数为0)或者(是否实现了SpringProxy接口)
   if (config.isOptimize() || config.isProxyTargetClass() 
            || hasNoUserSuppliedProxyInterfaces(config)) {
      // 获取需要被代理的目标类
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
         throw new AopConfigException("TargetSource cannot determine target class: " +
               "Either an interface or a target is required for proxy creation.");
      }
      // 如果要代理的目标类为接口,则直接使用jdk动态代理
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      // 否则使用cglib动态代理创建对象
      return new ObjenesisCglibAopProxy(config);
   }
   // 如果创建代理时不需要优化,未指定使用cglib,而且存在接口,则直接使用jdk创建代理对象
   else {
      return new JdkDynamicAopProxy(config);
   }
}

此处返回的就是一个通过策略类创建的代理对象,使用了【策略设计模式】,根据不同的条件使用不同的实现策略去创建代理对象,包括ObjenesisCglibAopProxy对应的策略实现以及JdkDynamicAopProxy对应的策略实现,他们都是AopProxy策略接口的实现类。

1.4、Spring生成代理的方式

Spring底层同时支持了两种生成代理的方式,那么cglib动态代理和jdk的动态代理究竟有什么区别呢?如果选择呢?

(1)使用方式不同

JDK动态代理的前提是被代理的类必须实现了某一个接口,而cglib不需要强制要求被代理类实现接口,可以是接口或者实现类。

(2)生成字节码的方式不同

JDK动态代理和cglib动态代理都是运行期间为被代理对象生成字节码,JDK是直接操作字节码,而cglib是使用了asm框架操作字节码。

(3)生成代理的效率不同

JDK生成字节码是直接使用接口,逻辑比较简单,效率稍高。而cglib生成字节码的逻辑比较复杂,所以生成代理的效率比较低。

(4)执行效率不同

JDK调用代理方法时,是需要通过反射调用,而cglib是通过fastClass机制直接调用方法,执行效率更高。

那么什么又是FastClass机制呢?为什么FastClass就这么快呢?

cglib执行代理方法的效率比jdk高,是因为cglib采用了fastClass机制,而JDK是通过反射调用的。

FastClass的原理:生成代理类时,为代理类和被代理类各生成一个Class,这个Class会为代理类或者被代理类的方法分配一个int类型的索引index,调用的时候将这个index当做一个入参,FastClass就可以直接通过索引定位到要调用的方法直接进行调用,所以省略了反射调用,执行效率高于JDK。使用类似于数据库索引的设计思想。

经过上面的buildAspectJAdvisors方法,获取了Advisor的缓存Map

四、AOP代理类生成

	// 获取需要增强的Advisor
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
	if (specificInterceptors != DO_NOT_PROXY) {
		this.advisedBeans.put(cacheKey, Boolean.TRUE);
		Object proxy = createProxy(
				bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
		this.proxyTypes.put(cacheKey, proxy.getClass());
		return proxy;
	}

	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return bean;
}

五、总结

Spring的AOP初始化过程比较复杂,将整体的大思路可以总结为如下几个步骤:

(1)注册后置处理器

给容器中导入一个用于拦截Bean床架的后置处理器,利用了Spring的扩展接口Aware及BeanPostProcessor接口。在注解版中,是通过@EnableAspectJAutoProxy来导入的,而在传统的xml中类似,只不过是通过配置的方式,但最终还是通过Spring的xml解析器将xml中配置的内容解析为了bean定义,并注册到Spring容器中了;

(2)创建增强之后的Bean

在容器启动的时候,会去创建所有使用注解标注的Bean组件,例如:@Component注解,而且会解析出标有@Aspect注解的切面类,并解析出类中定义的切点表达式。然后在初始化其他bean的时候,会根据切点表达式去匹配当前类是否需要增强,如果需要增强,则会对当前的类创建代理对象,创建代理对象是在Bean初始化完成之后做的;

(3)创建增强Bean的方式

在后置处理器中创建增强Bean时,会根据当前类是否实现了接口,代理类是否需要被优化,实现的接口是否为Spring原生的标记类接口,是否强制使用cglib方式等等条件去决定是使用cglib的方式还是jdk的方式,最后通过AopProxy策略接口的具体策略实现类去创建对应的代理类,然后加入到Spring容器中,整个初始化过程就执行完毕。

最后附上AOP流程图
https://www.processon.com/view/link/611bbd661efad412479f8387
在这里插入图片描述

附录

@RestController
public class TestController {
    @GetMapping("hello")
    public String hello() {
        return "hello spring security";
    }
}
@Aspect
@Component
public class AOPDemo {
    @Before("execution(* *.hello(..))")
    public void beforeNotify() {

        System.out.println("********@Before我是前置通知");
    }

    @After("execution(* *.hello(..))")
    public void afterNotify() {

        System.out.println("********@After我是后置通知");
    }

    @AfterReturning("execution(* *.hello(..))")
    public void afterReturningNotify() {

        System.out.println("********@AfterReturning我是返回后通知");
    }

    @AfterThrowing(" execution(* *.hello(..))")
    public void afterThrowingNotify() {

        System.out.println("********@AfterThrowing我是异常通知");
    }

    @Around(" execution(* *.hello(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        Object retvalue = null;
        System.out.println("我是环绕通知之前AAA");
        retvalue = proceedingJoinPoint.proceed();
        System.out.println("我是环绕通知之后BBB");
        return retvalue ;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值