5

Spring AOP 源码学习笔记

 2 years ago
source link: https://www.r9it.com/20221116/spring-aop.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Spring AOP 源码学习笔记


RockYang 2022-11-16 12 Spring 源码系列

刘慈欣 -《三体》

弱小和无知不是生存的障碍,傲慢才是。

# 01 - Spring ProxyFactory

ProxyFactory 是 Spring 实现的一个代理工厂类,它也是 Spring AOP 和 Spring 事务实现的底层技术支撑。平时我们在业务代码中也可以使用它,因为用它来创建动态代理对象真实太方便了!!!只需简单的三步:

  1. 创建 ProxyFactory 对象
  2. 设置被代理的 Target
  3. 获取代理对象

最关键的是,ProxyFactory 会自动判断你要使用 JDK 还是 CGLIB 来创建代理对象:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
   // 如果ProxyFactory的isOptimize为true,Spring认为 cglib比jdk动态代理要快
   // 或者isProxyTargetClass为true,
   // 或者被代理对象没有实现接口,
   // 或者只实现了SpringProxy这个接口
   // 那么则利用Cglib进行动态代理,但如果被代理类是接口,或者被代理类已经是进行过JDK动态代理而生成的代理类了则只能进行JDK动态代理

   // 其他情况都会进行JDK动态代理,比如被代理类实现了除SpringProxy接口之外的其他接口

   // 是不是在GraalVM虚拟机上运行
   if (!NativeDetector.inNativeImage() &&
         (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.");
      }
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
   }
   else {
      return new JdkDynamicAopProxy(config);
   }
}
Copied!

下面来看一个 ProxyFactory 的使用 Demo:

// 创建被代理对象
OrderService orderService = new OrderService();
// 创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
// 设置被代理对象
proxyFactory.setTarget(orderService);
// 添加一个 Advice, 实现切面方法
proxyFactory.addAdvice(new MethodInterceptor() {
   @Nullable
   @Override
   public Object invoke(@NotNull MethodInvocation invocation) throws Throwable
   {
      System.out.println("Around 前");
      Object proceed = invocation.proceed();
      System.out.println("Around 后");
      return proceed;
   }
});
// 获取代理对象
OrderService proxy = (OrderService)proxyFactory.getProxy();
// 代理对象执行方法
proxy.test();
Copied!

有木有觉得如丝般顺滑 O(∩_∩)O 哈哈~

# 02- AOP 的启动流程

  1. 通过 @EnableAspectJAutoProxy 注解引入 AspectJAutoProxyRegistrar 类, 从而注册一个 AnnotationAwareAspectJAutoProxyCreator BeanPostProcessor
  2. 具体 AOP 的入口在 AnnotationWareAspectJAutoProxyCreator 的父类 AbstractAutoProxyCreatorpostProcessAfterInitialization() 方法:
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    	if (bean != null) {
    		Object cacheKey = getCacheKey(bean.getClass(), beanName);
    		if (this.earlyProxyReferences.remove(cacheKey) != bean) {
    			return wrapIfNecessary(bean, beanName, cacheKey); // 包装成 AOP 代理对象
    		}
    	}
    	return bean;
    }
    
    Copied!
  3. 判断当前 bean 是否需要进行 AOP(是否存在匹配的 Advice),如果不需要则直接返回原对象,否则生成一个代理对象返回。

# 03 - 匹配 Advice 的具体逻辑

如何判断当前 Bean 是否存在匹配的 Advice 呢?具体实现过程如下:

  1. 先找到当前 Bean 容器中所有的 Advisor 类型的 Bean 对象,得到 advisors 列表

  2. 再从所有的切面中解析得到 Advisor(最终会封装成 InstantiationModelAwarePointcutAdvisorImpl 对象),将其加入 advisors 了列表中。

    protected List<Advisor> findCandidateAdvisors() {
    	// Add all the Spring advisors found according to superclass rules.
    	// 先找到所有Advisor类型的Bean对象
    	List<Advisor> advisors = super.findCandidateAdvisors();
    
    	// Build Advisors for all AspectJ aspects in the bean factory.
    	// 再从所有切面中解析得到Advisor对象
    	if (this.aspectJAdvisorsBuilder != null) {
    		advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    	}
    	return advisors;
    }
    
    // 具体封装 Advisor 逻辑在 ReflectiveAspectJAdvisorFactory::getAdvisor() 方法
    public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
    		int declarationOrderInAspect, String aspectName) {
    
    	validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
    
    	// 拿到当前方法所对应的Pointcut对象,但是注意:如果当前方法上是这么写的@After("pointcut()"),那么此时得到的Pointcut并没有去解析pointcut()得到对应的表达式
    	AspectJExpressionPointcut expressionPointcut = getPointcut(
    			candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
    	if (expressionPointcut == null) {
    		return null;
    	}
    
    	// expressionPointcut是pointcut
    	// candidateAdviceMethod承载了advice
    	return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
    			this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
    }
    
    Copied!
  3. 根据当前 beanClass 类型对所有候选的 Advisor 进行筛选,筛选之后对 Advisor 进行排序。

    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    	// 找到所有的Advisor
    	List<Advisor> candidateAdvisors = findCandidateAdvisors();
    	// 进行筛选
    	List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    
    	extendAdvisors(eligibleAdvisors);
    
    	// 对Advisor进行排序,按Ordered接口、@Order注解进行排序
    	if (!eligibleAdvisors.isEmpty()) {
    		eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    	}
    	return eligibleAdvisors;
    }
    
    Copied!

# 04 - 切面的筛选逻辑

具体实现在 AopUtils::canApply() 方法中

  1. 先判断类,如果类型不匹配则直接返回 false
    if (!pc.getClassFilter().matches(targetClass)) {
    	return false;
    }
    
    Copied!
  2. 判断方法匹配器 MethodMatcher, 遍历所有的方法,如果有一个方法匹配就返回 true
    for (Method method : methods) {
    	if (introductionAwareMethodMatcher != null ?
    			introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
    			// 判断方法是否匹配
    			methodMatcher.matches(method, targetClass)) {
    		return true;
    	}
    }
    
    Copied!

# 05 - 创建代理对象的过程

  1. 创建一个 ProxyFactory 对象
  2. 添加 Advisors(proxyFactory.addAdvisors(advisors)), 这里需要把匹配到的 Advice 包装成 Advisor 对象:
    • 如果本身就是 Advisor 对象,则直接强制转成 Advisor 对象并返回
    • 如果是 MethodInterceptor 对象,则包装成 DefaultPointcutAdvisor 对象。
    • 如果实现了具体的 AdvisorAdapter 接口(AfterReturningAdviceAdapter/MethodBeforeAdviceAdapter/ThrowsAdviceAdapter),将其包装成 DefaultPointcutAdvisor 对象
  3. 设置被代理对象(proxyFactory.setTargetSource(targetSource));

# 06 - 代理对象的执行过程(核心)

  1. 使用 ProxyFactory 创建代理对象之前,先要往 ProxyFactory 中添加 Advisor 。
  2. 代理对象在执行某个方法时,先判断一下如果是 hashCode() 或者 equals() 方法则不走代理,直接调用被代理对象的方法。
  3. 如果当前执行的方法需要走代理,则会先把 ProxyFactory 中所有的 Advisor 拿出来个当前的执行的方法进行匹配。
  4. 如果匹配的结果为空,则直接调用 target 对应的方法,将匹配成功的 Advisor 都适配成 MethodInterceptor 对象(AfterReturningAdviceAdapter/MethodBeforeAdviceAdapter/ThrowsAdviceAdapter),并按照顺序放入一个列表中,形成一个调用链(chain)。
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    
    Copied!
  5. 将代理对象(proxy),被代理对象(target), 当前调用方法(method),代理类和调用链(chain) 封装成一个 MethodInvocation 对象。
    MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    // Proceed to the joinpoint through the interceptor chain.
    retVal = invocation.proceed();
    
    Copied!
  6. 调用 invocation.proceed() 方法,开始执行各个 MethodInterceptor 以及被代理对象的对应方法,具体执行逻辑在 ReflectiveMethodInvocation::proceed() 方法中。
  7. 通过递归的方式,一层一层调用对应 Advisor 中的 MethodInterceptor 中的 Invoke() 方法,需要留意的是,这里会对 Advisor 再进行一次筛选:
    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.
    	// 不匹配则执行下一个MethodInterceptor
    	return proceed();
    }
    
    Copied!
  8. 直到执行完最后一个 MethodInterceptor 了,就会调用 invokeJoinpoint() 方法,从而执行被代理对象的当前方法
    // 当调用完了最后一个interceptor后就会执行被代理方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    	return invokeJoinpoint();
    }
    
    Copied!

如果您觉得本文对您有用,可以请作者喝杯咖啡。 如需商务合作请加微信(点击右边链接扫码): RockYang

donate_wechat.png
donate_alipay.png

版权申明 : 本站博文如非注明转载则均属作者原创文章,引用或转载请注明出处,如要商用请联系作者,谢谢。


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK