SpringAOP工作原理

使用方法

  • pom文件添加,包括aop和aspectj相关依赖
  • 配置类添加@EnableAspectJAutoProxy注解,开启事务「SpringBoot引入jar包自动装配开启」
  • 创建切面类,并使用注解@Aspect定义切面
  • 在切面类上添加,切入点、通知

关键概念

  • 切面-Aspect
  • 切点-Pointcut
  • 通知-Advice
  • 连接点-Join point
  • 增强器/通知器-Advisor

实现原理

开启AOP支持

@EnableAspectJAutoProxy注解,用于启用Spring容器对AspectJ切面的支持。它可以被用于@Configuration类中,用于启用自动代理功能,并自动创建AspectJ切面所需的代理对象。

@EnableAspectJAutoProxy注解提供了以下属性:

  • proxyTargetClass:默认为false,表示使用基于接口的JDK动态代理实现代理对象;如果设置为true,则使用CGLIB代理实现代理对象。「SpringBoot2默认使用CGLIB」
  • exposeProxy:默认为false,表示不会将当前代理对象暴露给AOP框架以外的代码;如果设置为true,则可以通过AopContext.currentProxy()方法访问当前代理对象。

当使用@EnableAspectJAutoProxy注解时,Spring会在运行时创建一个AnnotationAwareAspectJAutoProxyCreator对象,该对象会扫描Spring容器中的所有Bean,查找标有@Aspect注解的切面,并为它们自动创建代理对象。

创建AnnotationAwareAspectJAutoProxyCreator对象

@EnableAspectJAutoProxy注解通过@Import(AspectJAutoProxyRegistrar.class)注解将AnnotationAwareAspectJAutoProxyCreator.class登记到beandefinitionMap中,并在后续过程中将AnnotationAwareAspectJAutoProxyCreator创建为对象放入「单例池」中。

// AspectJAutoProxyRegistrar.class方法
public void registerBeanDefinitions(
		AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

  // 注册AnnotationAwareAspectJAutoProxyCreator
	AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

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

// AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)->注册
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
		BeanDefinitionRegistry registry, @Nullable Object source) {
	return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
解析@Aspect切面配置

AnnotationAwareAspectJAutoProxyCreator基类中的AbstractAutoProxyCreator,它实现了BeanPostProcessor接口,为Spring容器中的Bean提供了自动代理的功能。其中,postProcessBeforeInstantiation方法是AbstractAutoProxyCreator类中的一个方法,用于在实例化Bean之前进行处理。在这个方法里,Spring会扫描并查找标注@Aspect注解的切面类,并将其封装为Advisor「通知器」。

Advisor是Spring框架中AOP编程的核心概念之一,它代表着一个可以被应用于目标对象的切面。Advisor包含了一个Pointcut和一个Advice,Pointcut用于定义切入点,Advice用于定义在切入点处执行的逻辑。

在Spring中,Advisor可以被看作是一个切面的逻辑单元,它可以包含多个Pointcut和Advice,用于描述切面的行为。通常情况下,Spring框架中的Advisor会被应用于目标对象,从而实现AOP功能。

Spring框架中常用的Advisor类型包括:

  • BeforeAdvice:在目标方法执行之前执行逻辑。
  • AfterReturningAdvice:在目标方法执行完毕并返回结果之后执行逻辑。
  • AfterThrowingAdvice:在目标方法抛出异常时执行逻辑。
  • AroundAdvice:在目标方法执行前后都执行逻辑。

除了以上几种常用的Advisor类型,Spring框架还提供了许多其他类型的Advisor,例如IntroductionAdvisor用于实现接口的动态实现,PointcutAdvisor用于定义切入点等等。

在应用Advisor时,通常会使用Spring容器中的BeanPostProcessor接口的实现类,例如BeanNameAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator等,将Advisor应用于目标对象,并生成代理对象。通过配置不同的Advisor和切入点,可以实现各种不同的AOP功能,例如日志记录、性能统计、事务管理等等。

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation
// 关键实现
org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#shouldSkip
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
	// TODO: Consider optimization by caching the list of the aspect names
	List<Advisor> candidateAdvisors = findCandidateAdvisors();
	for (Advisor advisor : candidateAdvisors) {
		if (advisor instanceof AspectJPointcutAdvisor &&
				((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
			return true;
		}
	}
	return super.shouldSkip(beanClass, beanName);
}

// 获取所有「通知器」
org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
protected List<Advisor> findCandidateAdvisors() {
  // 先从SpringIOC容器中拿到已有的Advisor实现
	List<Advisor> advisors = super.findCandidateAdvisors();
	if (this.aspectJAdvisorsBuilder != null) {
    // 扫描所有bean,并查找@Aspect标注的bean,构建Advisor
		advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
	}
	return advisors;
}

// 查找并解析切面
public List<Advisor> buildAspectJAdvisors() {
  // ...省略其他代码
	List<Advisor> advisors = new ArrayList<>();
	String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
			this.beanFactory, Object.class, true, false);
  // 扫描所有bean
	for (String beanName : beanNames) {
    // ...省略其他代码
    Class<?> beanType = this.beanFactory.getType(beanName, false);
    // 判断是否被@Aspect标注
		if (this.advisorFactory.isAspect(beanType)) {
      // ...省略其他代码
      // 封装为Advisor
			advisors.addAll(this.advisorFactory.getAdvisors(factory));
      // ...省略其他代码
		}
	}
  // ...省略其他代码
	return advisors;
}

创建代理对象

首先来想一个问题,Spring如果要对一个类进行AOP,那么应该在什么时机进行?

SpringAOP是在其IOC的基础上进行扩展的功能,所以创建代理对象在bean的生命周期过程中进行最为合理,总共有两种情况:

  1. 正常情况下,在bean经历过实例化、属性赋值、初始化之后,Spring利用beanPostProcessor机制,使用AbstractAutoProxyCreator#postProcessAfterInitialization进行代理对象创建
  2. 另一种就是当bean存在「循环依赖」的情况,Spring为了解决循环依赖设置了三级缓存,bean在创建过程中会先往三级缓存中扔一个匿名实现,然后如果有需要就会使用AbstractAutoProxyCreator#getEarlyBeanReference进行代理对象创建

这两种方式,最终都是通过AbstractAutoProxyCreator#wrapIfNecessary方法创建代理对象。

// 第一步,在bean创建过程中预设实现方法
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// 第二步,如果有需要则执行方法调用
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// Quick check for existing instance without full singleton lock
	Object singletonObject = this.singletonObjects.get(beanName);
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		singletonObject = this.earlySingletonObjects.get(beanName);
		if (singletonObject == null && allowEarlyReference) {
			synchronized (this.singletonObjects) {
				// Consistent creation of early reference within full singleton lock
				singletonObject = this.singletonObjects.get(beanName);
				if (singletonObject == null) {
					singletonObject = this.earlySingletonObjects.get(beanName);
					if (singletonObject == null) {
						ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
						if (singletonFactory != null) {
              // 执行() -> getEarlyBeanReference(beanName, mbd, bean)中获取对象
							singletonObject = singletonFactory.getObject();
							this.earlySingletonObjects.put(beanName, singletonObject);
							this.singletonFactories.remove(beanName);
						}
					}
				}
			}
		}
	}
	return singletonObject;
}
// 打破循环依赖,将代理对象暴露出去
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
	Object exposedObject = bean;
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {        // 调用AbstractAutoProxyCreator#getEarlyBeanReference进行代理对象创建
			exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
		}
	}
	return exposedObject;
}
1. 获取所有Advisor

这里首先要从IOC容器中获取所有Advisor的实现,Advisor是SpringAOP的关键接口,其中包含了获取Advice的方法,代理对象的创建和方法调用都会用到。

// 调用链
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean
org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findCandidateAdvisors
org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans
// 关键方法
public List<Advisor> findAdvisorBeans() {
		String[] advisorNames = this.cachedAdvisorBeanNames;
		if (advisorNames == null) {
      // 从IOC容器中获取所有Advisor实现
			advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
					this.beanFactory, Advisor.class, true, false);
      // 缓存
			this.cachedAdvisorBeanNames = advisorNames;
		}
    // ...省略其他代码
    List<Advisor> advisors = new ArrayList<>();
		for (String name : advisorNames) {
      advisors.add(this.beanFactory.getBean(name, Advisor.class));
    }
  // ...省略其他代码
  return advisors;
}
2. 匹配Advisor

获取到所有Advisor之后,Spring怎么知道当前这个对象「目标对象」是需要进行AOP增强的呢?这时候就要对目标对象的所有方法进行扫描。

首先从Advisor中拿到持有的Pointcut,然后再根据Pointcut拿到持有的MethodMatcher。Spring要对目标对象的所有方法进行扫描,使用Pointcut的MethodMatcher

  • 扫描「目标对象所有的方法」并匹配其适用的Advisor,如果没有匹配到说明目标对象不符合
  • 这里边有几个比较关键的接口:
    • Pointcut
    • MethodMatcher
    • IntroductionAwareMethodMatcher「是Advisor其中一种实现方式体系的」

该方法会从Pointcut中获取持有的MethodMatcher,并进行匹配,不同的场景有各自的实现。

其中常用的表达式方式的实现是AspectJExpressionPointcut。

Spring事务实现是TransactionAttributeSourcePointcut。

// 调用链
org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean
org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply
org.springframework.aop.support.AopUtils#findAdvisorsThatCanApply
org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Advisor, java.lang.Class<?>, boolean)
org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean)
// 关键方法
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
		Assert.notNull(pc, "Pointcut must not be null");
		if (!pc.getClassFilter().matches(targetClass)) {
			return false;
		}
    // 从Pointcut中获取MethodMatcher
		MethodMatcher methodMatcher = pc.getMethodMatcher();
		if (methodMatcher == MethodMatcher.TRUE) {
			// No need to iterate the methods if we're matching any method anyway...
			return true;
		}

		IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
		if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
			introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
		}

		Set<Class<?>> classes = new LinkedHashSet<>();
		if (!Proxy.isProxyClass(targetClass)) {
			classes.add(ClassUtils.getUserClass(targetClass));
		}
		classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

		for (Class<?> clazz : classes) {
			Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
      // 遍历当前bean的所有方法
			for (Method method : methods) {
        // 使用从Pointcut中获取到的MethodMatcher进行匹配
				if (introductionAwareMethodMatcher != null ?
						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
						methodMatcher.matches(method, targetClass)) {
					return true;
				}
			}
		}

		return false;
	}
2. 创建代理对象
  • 创建代理类,将增强逻辑的通用接口,织入代理对象
    • java动态代理方式
    • cglib动态代理方式
  • 将创建好的代理对象放入IOC单例池中
问题一:Spring如何判断当前bean是否需要被代理?

如果第一步获取不到Advisor,就说明当前bean不需要被代理

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    /** 不能被代理的类 */
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    /** 不能被代理的类 */
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
    // Create proxy if we have advice.
    /** 获取当前类的所有通知 */
    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;
}

那么,如何判定当前bean是否存在相关Advice呢?

调用代理对象

在这里插入图片描述

Cglib动态代理模式下,运行代理对象的方法会执行DynamicAdvisedInterceptor#intercept方法。

Jdk动态代理模式下,运行代理对象方法会执行JdkDynamicAopProxy#invoke方法。

关键方法:

  • Cglib
    • org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept
    • org.springframework.aop.framework.CglibAopProxy.CglibMethodInvocation#proceed
  • Jdk
    • org.springframework.aop.framework.JdkDynamicAopProxy#invoke
  • org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
  • org.aopalliance.intercept.MethodInterceptor#invoke

执行逻辑:

  1. DynamicAdvisedInterceptor#intercept方法创建CglibMethodInvocation对象,并将目标类对象通知调用链数组等关键信息传递过去。
  2. CglibMethodInvocation#proceed通过调用父类ReflectiveMethodInvocation#proceed方法进行处理
  3. 后面JDK和Cglib两种代理模式处理逻辑就一样了
  4. ReflectiveMethodInvocation#proceed会遍历通知调用链数组取出具体的通知实现类,然后执行其#invoke方法实现通知中增强逻辑的调用
  5. 通知调用链数组都完成调用之后,才会执行连接点「被AOP环切的方法」的调用

那么在定义多个通知的情况下是如何保证增强逻辑的执行顺序呢?

其实通知执行整个过程是一个递归调用,具体分为两类:

  1. 前置类通知执行:Before、Around「前半部分」
  2. 后置类通知执行:After、AfterReturning、AfterThrowing、Around「后半部分」

前置类通知执行的时候会先执行增强逻辑,然后再继续调用ReflectiveMethodInvocation#proceed方法看是否存在其他通知

后置类通知执行的时候会先递归调用ReflectiveMethodInvocation#proceed方法看是否存在其他通知,直到整个通知调用链数组中没有通知可以调用了,那么就可以执行目标类的连接点方法,然后每个后置类通知的增强逻辑才会被执行。

其中Around方法比较特殊,其在执行过程中会将CglibMethodInvocation对象封装成ProceedingJoinPoint,然后调用通知方法,先执行前半部分增强逻辑,然后执行ProceedingJoinPoint#proceed「ReflectiveMethodInvocation#proceed」去调用其他通知,最后等调用链方法依次返回后,对应的后置类通知的增强逻辑就会被执行。

public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
public Object proceed() throws Throwable {
   // We start with an index of -1 and increment early.
   /** 如果没有通知器可以调用了,就执行目标对象的连接点方法 */
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }

   /** 依次处理每一个通知 */
   Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
   if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      // Evaluate dynamic method matcher here: static part will already have
      // been evaluated and found to match.
      InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
      if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
         return dm.interceptor.invoke(this);
      }
      else {
         // Dynamic matching failed.
         // Skip this interceptor and invoke the next in the chain.
         return proceed();
      }
   }
   else {
      /** 执行具体的通知类型调用 */
      // It's an interceptor, so we just invoke it: The pointcut will have
      // been evaluated statically before this object was constructed.
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}}

代理对象的两种实现方式

如何选择?
  1. 设置@EnableAspectJAutoProxyproxyTargetClass属性为true就会使用cglib动态代理方式
  2. proxyTargetClass属性默认false-jdk动态代理方式
  3. 如果使用jdk动态代理方式那么被代理类必须要实现接口
  4. Springboot2版本自动装配时已经将默认方式改为cglib了
差异

Jdk是将被代理对象作为子类来实现,且必须实现接口

public final class Service4j$Proxy extends Proxy implements Service4j {
  // 被代理方法
  public final void doService() throws  {
        try {
            // 执行父类「JdkDynamicAopProxy#invoke」方法
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
}

Cglib是将被代理对象作为父类

public class Service4c$$EnhancerBySpringCGLIB$$414167ef extends Service4c implements SpringProxy, Advised, Factory {
  // 被代理方法
  public final void doService() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            // 调用「DynamicAdvisedInterceptor#intercept」方法
            var10000.intercept(this, CGLIB$doService$1$Method, CGLIB$emptyArgs, CGLIB$doService$1$Proxy);
        } else {
            super.doService();
        }
    }
}
Jdk动态代理

org.springframework.aop.framework.JdkDynamicAopProxy#invoke

第一步:创建代理类对象
  1. 首先会获取当前类对应切面的所有通知器Advisor
  2. 将通知器封装为jdk动态代理的JdkDynamicAopProxy,此类是一个InvocationHandler类型,运行时会调用其invoke方法。
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
   Assert.notNull(config, "AdvisedSupport must not be null");
   if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
      throw new AopConfigException("No advisors and no TargetSource specified");
   }
   // 当前bean方法对应的所有通知
   this.advised = config;
   this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
   findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces);
}
  1. 使用JdkDynamicAopProxy对象创建代理对象
public Object getProxy(@Nullable ClassLoader classLoader) {
   if (logger.isTraceEnabled()) {
      logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
   }
   // 创建代理对象,this等于JdkDynamicAopProxy,内含通知逻辑
   return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
第二步:调用代理对象方法
  1. 代理对象执行JdkDynamicAopProxy#invoke方法
  2. invoke方法获取其封装的所有通知器Advisor
  3. 将所有Advisor封装为通知调用链数组
  4. 调用ReflectiveMethodInvocation#proceed方法
  5. 遍历调用链数组执行通知器的具体通知方法「普通方法无通知」
  6. 调用目标对象的方法
Cglib动态代理

org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept

第一步:创建代理类对象
  1. 首先会获取当前类对应切面的所有通知器Advisor
  2. 将通知器封装为cglib的拦截器
  3. 将拦截器封装为回调数组
  4. 创建代理对象
  5. 将回调数组设置给代理对象
第二步:调用代理对象方法
  1. 代理对象方法调用拦截器的intercept方法
  2. intercept方法获取其封装的所有通知器Advisor
  3. 将所有Advisor封装为通知调用链数组
  4. 调用ReflectiveMethodInvocation#proceed方法
  5. 遍历调用链数组执行通知器的具体通知方法「普通方法无通知」
  6. 调用目标对象的方法

核心类-TODO

AnnotationAwareAspectJAutoProxyCreator

在这里插入图片描述

AnnotationAwareAspectJAutoProxyCreator是一个BeanPostProcessor,使用@EnableAspectJAutoProxy注解会将这个类登记到IOC容器中。

在bean声明周期的初始化阶段会调用这个后置处理器,其中用来判定一个bean是否需要进行aop的关键方法是

思考

为什么需要两种创建代理类的方式?哪种更好?
  1. jdk代理方式要求被代理对象必须要实现接口
  2. Cglib不要求代理对象实现接口,目前springboot2已经将cglib调整为默认实现。
为什么不直接将增强逻辑写入的代理类中?而是采用织入接口方式?
  1. 扩展性更强且解耦,可以各种实现而又不必改变生成字节码的逻辑
  2. 代理类生成的字节码更小
Cglib创建代理类的时候是如何将增强接口的实现类对象设置进去的?
  1. 首先cglib会将切面的通知做成各种callbacks

    private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
        // ···省略其他代码
    		// Choose an "aop" interceptor (used for AOP calls).
        // 这里将通知封装成「通用的AOP拦截器」
    		Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
        // ···省略其他代码
    		Callback[] mainCallbacks = new Callback[] {
            // 将拦截器封装成回调数组
    				aopInterceptor,  // for normal advice
    				targetInterceptor,  // invoke target without considering advice, if optimized
    				new SerializableNoOp(),  // no override for methods mapped to this
    				targetDispatcher, this.advisedDispatcher,
    				new EqualsInterceptor(this.advised),
    				new HashCodeInterceptor(this.advised)
    		};
    
    		Callback[] callbacks;
        // ···省略其他代码
        if(···) { 
          callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
          // ···省略其他代码
        } else {
          callbacks = mainCallbacks;
        }
    		return callbacks;
    	}
    
  2. 然后创建代理对象实例,并将所有callbacks设置给对象

    protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
        //创建代理类
    		Class<?> proxyClass = enhancer.createClass();
    		Object proxyInstance = null;
    
    		if (objenesis.isWorthTrying()) {
    			try {
            //创建代理对象
    				proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
    			}
    			catch (Throwable ex) {
    				logger.debug("Unable to instantiate proxy using Objenesis, " +
    						"falling back to regular proxy construction", ex);
    			}
    		}
        //···省略其他代码
        //设置通知回调
    		((Factory) proxyInstance).setCallbacks(callbacks);
    		return proxyInstance;
    	}
    
  3. 再看生成的代理类方法会发现,实际调用的是this.CGLIB$CALLBACK_0#intercept 方法。

    //代理对象生成的方法
    public final void doService() {
        //org.springframework.cglib.proxy.MethodInterceptor
        //此处设置一个MethodInterceptor的实现类
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
    
        //如果说有实现类,则直接调用实现类的intercept方法
        if (var10000 != null) {
            var10000.intercept(this, CGLIB$doService$1$Method, CGLIB$emptyArgs, CGLIB$doService$1$Proxy);
        //如果说没有对应的实现类,则调用「被代理类」的实现方法
        } else {
            super.doService();
        }
    }
    //设置回调方法
    public void setCallbacks(Callback[] var1) {
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
            this.CGLIB$CALLBACK_1 = (MethodInterceptor)var1[1];
            this.CGLIB$CALLBACK_2 = (NoOp)var1[2];
            this.CGLIB$CALLBACK_3 = (Dispatcher)var1[3];
            this.CGLIB$CALLBACK_4 = (Dispatcher)var1[4];
            this.CGLIB$CALLBACK_5 = (MethodInterceptor)var1[5];
            this.CGLIB$CALLBACK_6 = (MethodInterceptor)var1[6];
        }
    
  4. this.CGLIB$CALLBACK_0又是什么呢?通过调试发现其指向的就是DynamicAdvisedInterceptor 对象,也就是说最终代理类执行的就是org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept方法

在这里插入图片描述

被代理类中的普通方法是如何执行的?
  1. 首先看普通方法生成的代理类代码
//代理类生成的普通方法
public final void doPlain() {
    MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
    if (var10000 == null) {
        CGLIB$BIND_CALLBACKS(this);
        var10000 = this.CGLIB$CALLBACK_0;
    }

    if (var10000 != null) {
        var10000.intercept(this, CGLIB$doPlain$1$Method, CGLIB$emptyArgs, CGLIB$doPlain$1$Proxy);
    } else {
        super.doPlain();
    }
}
  1. 可以看到调用的依然是拦截器的org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept方法
  2. 只不过在拦截器执行的时候会获取所有的通知器,而普通方法是没有通知的,所以就会直接执行目标方法

代理类织入的是接口方法,那么调用链如何控制执行顺序?又是在什么情况下调用目标方法的?

关键方法:

  • org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept
  • org.springframework.aop.framework.CglibAopProxy.CglibMethodInvocation#proceed
  • org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
  • org.aopalliance.intercept.MethodInterceptor#invoke

执行逻辑:

  1. 获取目标类对象、目标方法以及对应的通知调用链数组
  2. DynamicAdvisedInterceptor#intercept方法创建CglibMethodInvocation对象,并将目标类对象通知调用链数组等关键信息传递过去,然后以自身为参数调用proceed方法「主要目的是为了维护通知调用链数组下标计数器」
  3. CglibMethodInvocation#proceed通过调用父类ReflectiveMethodInvocation#proceed方法进行处理
  4. ReflectiveMethodInvocation#proceed会遍历通知调用链数组取出具体的通知实现类,然后执行其#invoke方法实现通知中增强逻辑的调用
  5. 通知调用链数组都完成调用之后,才会执行连接点「被AOP环切的方法」的调用

当目标方法是普通方法的时候,那么对应的通知调用链数组是没有通知的,所以就会直接调用目标对象的普通方法。

那么在定义多个通知的情况下是如何保证增强逻辑的执行顺序呢?

其实通知执行整个过程是一个递归调用,具体分为两类:

  1. 前置类通知执行:Before、Around「前半部分」
  2. 后置类通知执行:After、AfterReturning、AfterThrowing、Around「后半部分」

前置类通知执行的时候会先执行增强逻辑,然后再继续调用ReflectiveMethodInvocation#proceed方法看是否存在其他通知

后置类通知执行的时候会先递归调用ReflectiveMethodInvocation#proceed方法看是否存在其他通知,直到整个通知调用链数组中没有通知可以调用了,那么就可以执行目标类的连接点方法,然后每个后置类通知的增强逻辑才会被执行。

其中Around方法比较特殊,其在执行过程中会将CglibMethodInvocation对象封装成ProceedingJoinPoint,然后调用通知方法,先执行前半部分增强逻辑,然后执行ProceedingJoinPoint#proceed「ReflectiveMethodInvocation#proceed」去调用其他通知,最后等调用链方法依次返回后,对应的后置类通知的增强逻辑就会被执行。


为什么Spring事务存在类内方法调用情况下会导致事务失效?
  1. 同一个类内的「无事务声明方法」调用「事务声明方法」情况下才会导致事务失效
  2. 调用链:代理类#无事务方法->目标类#无事务方法->目标类#带事务方法
  3. 是否应用事务增强关键点在于代理类,如果从代理类被调用的第一个方法是无事务增强的,那么后续整个链条都不会存在动态代理产生的事务增强。「除非后续调用链方法能重新调用代理类的事务方法,也就是自我注入情况」
  4. 上述方法调用链是没有触发事务增强逻辑的,所以属于是无事务控制状态,只有直接调用「代理类#带事务方法」才会触发事务增强逻辑
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值