FIT AOP 源码导读与实现机制解析

58 阅读5分钟

本文将讲解 FIT 框架中 AOP 的实现机制与调用链路,涉及 IoC 生命周期接入、拦截器解析与排序、切点匹配、代理创建与方法调用编排等核心组件。

主要涉及内容:

  • 代码根目录:framework/fit/java
  • 主要模块:fit-aop/fit-aop(AOP 抽象与通用实现)、fit-aop/fit-aop-aspect(Aspect 切面实现)、fit-aop/fit-aop-bytebuddy(ByteBuddy 代理)、fit-api(公共 API)

1. AOP 何时介入:IoC 生命周期与 Bean 装饰

入口类:modelengine.fitframework.aop.AopInterceptor(实现 BeanLifecycleInterceptor

关键位置:

  • isInterceptionRequired(BeanMetadata metadata):判定当前 Bean 是否需要 AOP 代理。源码使用所有 MethodInterceptorResolvereliminate(metadata) 进行“短路淘汰”,当存在一个 resolver 认为该 bean 不应参与(返回 true),则整体不做 AOP;只有当所有 resolver 都不淘汰时才进入 AOP。
  • decorate(BeanLifecycle lifecycle, Object bean)
    1. 先调用下游生命周期装饰得到 initializedBean
    2. 聚合所有 MethodInterceptorResolver 的结果,生成拦截器列表并按 MethodInterceptorComparator 排序;
    3. 若非空,则构造 InterceptSupport
    4. 委派给 createAopProxy 选择合适的 AopProxyFactory 创建代理;否则直接返回原对象。

2. 拦截器模型与排序

核心接口:modelengine.fitframework.aop.interceptor.MethodInterceptor

  • MethodPointcut getPointCut():声明本拦截器适配的方法集合(运行前由匹配器筛选得到具体 Method 集合)。
  • Object intercept(MethodJoinPoint jp):拦截器执行主体。

抽象基类:support.AbstractMethodInterceptor

  • 内部维护 DefaultMethodPointcut,可在构造时注入若干 MethodMatcher 以约束匹配范围。

排序器:support.MethodInterceptorComparator

  • 主序基于拦截器“种类”优先级:Around > Before > After > AfterReturning > AfterThrowing
  • 次序通过 OrderResolver(SPI 可组合)解析 @Order 值;若解析失败取默认 Order.MEDIUM

相同连接点上的不同拦截器将按上述规则全局稳定排序,既保证 Advice 类型优先级,又支持业务自定义次序。

3. 切点与匹配:从“表达式”到“具体方法”

接口:MethodPointcut 与其默认实现 support.DefaultMethodPointcut

  • matchers():返回 MethodMatcherCollection,用于收集一组 MethodMatcher 条件,所有 matcher 均通过时才认为方法匹配。
  • add(Class<?>):为某个类收集“所有可匹配的方法”:
    • 先用每个 matcher 的 couldMatch(clazz) 做类级短路;
    • 再遍历类、父类、接口上的 declaredMethods,逐个调用 matcher 的 match(method)
    • 全部通过后,校验方法不能是 finalprivate,否则抛出异常;
    • 通知各 matcher 的 choose(method, result),最终把方法加入 pointcut。

常用匹配器示例:

  • AccessibleMethodMatcherMethodMatcher.accessible() 工厂方法返回)
  • AnnotationMethodMatcherMethodMatcher.annotation(...)
  • SpecifiedMethodMatcherMethodMatcher.specified(method)

Aspect 风格的切点表达式解析位于 fit-aop-aspect 模块;解析产物最终都会体现在 MethodPointcutmethods() 具体集合上,供调用时过滤。

4. 拦截器解析(Resolver)与切面支持

入口:MethodInterceptorResolver

  • 框架在 AopInterceptor 中组装多个 resolver:
    • 内置:AsyncInterceptorResolverCacheInterceptorResolver
    • SPI 加载:AspectInterceptorResolver
  • resolve(BeanMetadata, bean) 产出一组 MethodInterceptoreliminate(metadata) 返回 true 则表示该 bean 不参与 AOP。

切面解析:fit-aop-aspect 模块

  • interceptor/aspect/interceptor/AspectInterceptorResolver
    • 收集当前容器中标注 @AspectBeanFactory,区分 @Scope(PLUGIN)@Scope(GLOBAL) 两类来源(插件级与全局级)。
    • 遍历切面方法,交给若干 MethodInterceptorFactory
      • AspectBefore/After/Around/AfterReturning/AfterThrowingInterceptorFactory
      • 工厂负责识别方法是否带对应注解、抽取切点表达式与参数绑定信息,创建具体的 AdviceMethodInterceptor 实例(如 AspectAfterInterceptor)。
  • eliminate(metadata):若当前 metadata 本身带 @Aspect,返回 true,避免切面 bean 被 AOP。

Resolver 负责“把注解/表达式世界”转换成运行期可执行的 MethodInterceptor 列表,这是 AOP 生效的来源端。

5. 代理与调用链:如何把拦截器串起来

统一代理接口:fit-apiaop.proxy.FitProxy

  • Class<?> $fit$getActualClass():运行期可从代理对象取回被代理真实类型。

代理工厂聚合:aop.proxy.AopProxyFactories

  • 通过 SPI 收集所有 AopProxyFactory 并按 @Order 排序。

两种代理方式:

  1. JDK 动态代理:support.JdkDynamicAopProxyFactory
    • support(targetClass):当目标是接口、已是 Proxy、或 Lambda 类时返回 true。
    • createProxy(support):以公共子类加载器创建 Proxy,接口数组为 { targetClass, FitProxy },回调为 JdkDynamicProxy
  2. ByteBuddy 子类代理:fit-aop-bytebuddy/.../ByteBuddyAopProxyFactory
    • 对任意目标类型 support() 返回 true(兜底方案)
    • 使用 ByteBuddy 生成 targetClass 的子类,并 implement(FitProxy),所有 isMethod()InvocationHandlerAdapter.of(new JdkDynamicProxy(support)) 统一接管。
    • 使用 ReflectionFactoryInstantiator 无参构造进行实例化,并以 ConcurrentHashMap 缓存已生成的代理类。

调用核心:support.AbstractAopProxy

  • 关键方法:protected Object invoke(Object proxy, Method method, Object[] args, ProxiedInvoker proxiedInvoker)
    • 拦截 $fit$getActualClass 特殊方法:直接返回真实类型。
    • 过滤出当前方法对应的拦截器列表:基于拦截器的 getPointCut().methods() 包含关系
    • 若为空:调用 proxiedInvoker.invoke(...) 直接执行被代理对象(JDK 代理中由 ReflectionUtils.invoke 完成;ByteBuddy 通过 InvocationHandlerAdapter 转给同一个回调)。
    • 若非空:构造责任链:
      • 末尾追加 ProxiedInterceptor,其 intercept(jp) 通过 proxiedInvoker 最终调用被代理方法。
      • 自尾向头包装 DefaultMethodJoinPointDefaultMethodInvocation,形成“嵌套的 nextInvocation”。
      • 触发链路:调用第一个拦截器的 intercept(joinPoint)

JDK 回调:support.JdkDynamicProxy(实现 InvocationHandler

  • invoke(...) 中调用父类 AbstractAopProxy#invoke(...)
  • 特殊处理 toString():当目标对象尚未初始化(懒加载 Supplier 还未返回)时,返回 "$fit$" + targetClass + "#toString()" 的占位字符串,避免空指针与递归问题。FitProxy.$fit$getActualClass() 则在未初始化时返回 null(见 AbstractAopProxy#$fit$getActualClass)。

6. 关键约束与边界条件

  • 目标方法不能是 finalprivateDefaultMethodPointcut#validateMethod 明确校验并抛错。
  • 排序稳定性:先 Advice 类型优先级,再 @Order 值;OrderResolver 通过 SPI 组合,可扩展解析策略。
  • 资源加载:拦截器解析器 MethodInterceptorResolverOrderResolverAopProxyFactory 皆通过 ServiceLoader 扩展;确保相应模块提供 META-INF/services 声明。
  • 类加载器:两种代理都使用 ClassLoaderUtils.getCommonChildClassLoader(...) 获取公共子类加载器,确保代理类与 FitProxy、目标类可见性一致。

7. 端到端调用链总览

  1. Bean 创建完成 → AopInterceptor.decorate(...)
    • 聚合 Resolver → 产出并排序 List<MethodInterceptor>
    • 构造 DefaultInterceptSupport(targetClass, targetSupplier, interceptors)
    • AopProxyFactories#getAll() 选择第一个 support(targetClass) 的工厂创建代理(先 JDK,再 ByteBuddy,取决于 @Order)。
  2. 方法被调用 → 进入 JdkDynamicProxy#invoke(...) 或 ByteBuddy 生成类的 InvocationHandlerAdapter,再委派到 AbstractAopProxy#invoke(...)
  3. AbstractAopProxy#invoke(...)
    • 找到匹配当前 Method 的拦截器子集;若空直接调用目标;
    • 子集尾部加 ProxiedInterceptor;自尾向头包裹 MethodJoinPoint 链;
    • 调用第一个拦截器的 intercept(joinPoint),在各拦截器内部按需 joinPoint.proceed() 继续推进,直至最终执行目标方法。

8. 最小可运行示例

示例结构:

// 1) 业务接口与实现
package demo;

public interface HelloService {
  String hello(String name);
}

package demo;

public class HelloServiceImpl implements HelloService {
  @Override
  public String hello(String name) {
    return "hello, " + name;
  }
}

// 2) 切面定义(Before 通知)
package demo;

import modelengine.fitframework.aop.annotation.Aspect;
import modelengine.fitframework.aop.annotation.Before;
import modelengine.fitframework.annotation.Scope;

@Aspect(scope = Scope.PLUGIN)
public class LogAspect {
  @Before("execution(* demo.HelloService.hello(..))")
  public void logBefore() {
    System.out.println("[Before] about to call hello(..)");
  }
}

输出

[Before] about to call hello(..)
hello, FIT