likes
comments
collection
share

Sermant源码(一)agent premain挂载

作者站长头像
站长
· 阅读数 37

前言

本章基于Sermant2.0.0版本分析sermant-agent通过premain方式挂载。

  1. premain主流程;
  2. 类加载流程;
  3. 运行时被增强方法执行流程;
  4. sermant类加载器设计;

一、AgentLauncher

AgentLauncher是agent的启动入口由AppClassLoader加载。

AgentLauncher#launchAgent:

入参isDynamic,false-premain应用启动挂载,true-agentmain增强应用运行挂载,后续主要分析premain方式。

  1. 将god包加入BootStrap顶层类加载器,这个很重要;
  2. 根据agent参数和bootstrap.properties(优先级前者大于后者),确定artifactserviceNameappName等属性;
  3. installAgent主方法;
private static void launchAgent(String agentArgs, Instrumentation instrumentation, boolean isDynamic) {
    try {
        final Map<String, String> agentArgsMap = AgentArgsResolver.resolveAgentArgs(agentArgs);

        // god包加入Bootstrap顶层类加载器
        installGodLibs(instrumentation);

        // 读取bootstrap.properties artifact、appName、appType、serviceName,注入bootArgsMap
        String agentPath = agentArgsMap.get(BootConstant.AGENT_PATH_KEY);
        Map<String, Object> bootArgsMap = new HashMap<>(agentArgsMap);
        BootArgsBuilder.build(bootArgsMap, agentPath);

        String artifact = (String) bootArgsMap.get(BootConstant.ARTIFACT_NAME_KEY);

        // 主方法入口
        installAgent(instrumentation, isDynamic, artifact, bootArgsMap, agentPath);

        // 执行命令(忽略)
        executeCommand(artifact, agentArgsMap);
    } catch (Exception exception) {
        LOGGER.log(Level.SEVERE, "Loading sermant agent failed: " + exception.getMessage());
    }
}

AgentLauncher#installAgent:比较关键

  1. SermantManager(god) 校验artifact是否被安装过;
  2. SermantManager创建一个SermantClassLoader(god)
  3. SermantClassLoader加载AgentCoreEntrance(core) ,AgentCoreEntrance真实执行启动逻辑;
private static void installAgent(Instrumentation instrumentation, boolean isDynamic, String artifact,
        Map<String, Object> argsMap, String agentPath) {
    // SermantManager由god提供,Bootstrap类加载器加载,控制一个artifact只能安装一次
    if (!SermantManager.checkSermantStatus(artifact)) {
        // SermantClassLoader由god提供
        // 每个artifact创建一个SermantClassLoader,对于premain挂载,只需要考虑一个SermantClassLoader
        // SermantClassLoader加载core包(包含byte-buddy),父类加载器是AppClassLoader
        SermantClassLoader sermantClassLoader = SermantManager.createSermant(artifact, getCoreLibUrls(
                agentPath));

        // SermantClassLoader加载AgentCoreEntrance启动
        sermantClassLoader.loadClass(BootConstant.AGENT_CORE_ENTRANCE_CLASS)
                .getDeclaredMethod(BootConstant.AGENT_INSTALL_METHOD, String.class, Map.class,
                        Instrumentation.class, boolean.class)
                .invoke(null, artifact, argsMap, instrumentation, isDynamic);
        LOGGER.log(Level.INFO, "Load sermant done, artifact is: " + artifact);
        SermantManager.updateSermantStatus(artifact, true);
    } else {
        LOGGER.log(Level.INFO, "Sermant for artifact is running, artifact is: " + artifact);
    }
}

二、SermantClassLoader

sermant-agent.jar通过provided方式依赖god包。

god包中的SermantClassLoader负责加载core包中的类。

Sermant源码(一)agent premain挂载

Sermant支持挂载多次,通过artifact区分,artifact默认为default。

通过顶层BootStrap类加载器来加载god包下的SermantManager管理n个artifact下的SermantClassLoader

Sermant源码(一)agent premain挂载

SermantClassLoader#loadClass:

SermantClassLoader继承URLClassLoader,支持从core包中加载类,父类加载器是AppClassLoader。

SermantClassLoader优先从core包中找类,然后才走双亲委派。

Sermant源码(一)agent premain挂载

三、启动主流程

AgentCoreEntrance#install:启动主流程如下。

public static void install(String artifact, Map<String, Object> argsMap, Instrumentation instrumentation,
        boolean isDynamic) throws Exception {
    if (isDynamic) {
        agentType = AgentType.AGENTMAIN.getValue();
    }
    artifactCache = artifact;
    // 一个artifact对应一个AdviserInterface
    adviserCache = new DefaultAdviser();
    // 创建一个默认的logger(忽略)
    LoggerFactory.initDefaultLogger(artifact);
    // 类加载器管理 初始化
    ClassLoaderManager.init(argsMap);
    // 日志系统 初始化 ---> SermantBridgeHandler
    LoggerFactory.init(artifact);
    // Build the path index by startup configuration
    BootArgsIndexer.build(argsMap);
    // 加载config.properties
    ConfigManager.initialize(argsMap);
    // 加载BaseOperation类
    OperationManager.initOperations();
    // 加载BaseService类
    ServiceManager.initServices();
    // 事件系统 初始化
    EventManager.init();
    // 字节码增强 初始化
    ByteEnhanceManager.init(instrumentation);
    // 插件 初始化
    PluginSystemEntrance.initialize(isDynamic);
    // 注册本次增强的adviser
    AdviserScheduler.registry(adviserCache);
    // premain方式启动,使用插件执行增强
    if (!isDynamic) {
        ByteEnhanceManager.enhance();
    }
    // 发布Sermant启动事件
    FrameworkEventCollector.getInstance().collectAgentStartEvent();
    // 发布LOAD_COMPLETE通知
    if (NotificationManager.isEnable()) {
        NotificationManager.doNotify(new NotificationInfo(SermantNotificationType.LOAD_COMPLETE, null));
    }
}

按照上面的主流程可以划分为四部分:

  1. 类加载器初始化,ClassLoaderManager.init;
  2. 业务服务初始化,LoggerFactory-日志,ConfigManager-配置,OperationManager-BaseOperation类,ServiceManager-BaseService类,EventManager-事件;
  3. 字节码增强安装,PluginSystemEntrance.initialize-插件初始化,AdviserScheduler-注册本次增强adviser,ByteEnhanceManager.enhance-安装增强;
  4. 后置处理,FrameworkEventCollector发送启动事件给backend,NotificationManager发布LOAD_COMPLETE给框架内部;(这部分忽略)

四、类加载器管理初始化

下图是Sermant的整体类加载器结构。

Sermant源码(一)agent premain挂载

对应产物结构:

Sermant源码(一)agent premain挂载

ClassLoaderManager#init:

  1. SermantClassLoader在AgentLauncher中构造,只能加载core.jar,这里增加了common.jar;
  2. FrameworkClassLoader用于加载implement.jar,Sermant框架对于core包中的接口实现,可以引入三方依赖,如zk、nacos,但是不会与用户的类冲突。父类加载器是SermantClassLoader
  3. 创建PluginClassFinder,管理插件和插件类加载器PluginClassLoader,插件可能还有service模块,涉及ServiceClassLoader,这个在插件初始化中细看;

Sermant源码(一)agent premain挂载

五、日志初始化

LoggerFactory#init:创建Logger实例。

通过FrameworkClassLoader加载框架implement.jar中的LoggerFactoryImpl,并执行init方法。

Sermant源码(一)agent premain挂载

LoggerFactoryImpl#getLogger:核心点在于Logger实例加入了SermantBridgeHandler

Sermant源码(一)agent premain挂载

SermantBridgeHandler可以将WARNERROR级别日志,通过事件系统发送到backend。

Sermant源码(一)agent premain挂载

在backend可以看到日志类型事件。

Sermant源码(一)agent premain挂载

六、配置初始化

ConfigManager#initialize:JDK-SPI使用FrameworkClassLoader加载implement包下的LoadConfigStrategy实现,目前官方支持properties和yaml格式配置文件。

Sermant源码(一)agent premain挂载

默认配置文件是agent/config.properties。

Sermant源码(一)agent premain挂载

ConfigManager#doLoadConfig:JDK-SPI用SermantClassLoader加载core包中的BaseConfig实现,将配置注入BaseConfig。

Sermant源码(一)agent premain挂载

BaseConfig是一个标记接口。

Sermant源码(一)agent premain挂载

通过ConfigTypeKey注解修饰,配合配置解析,将配置项注入BaseConfig实现类。

Sermant源码(一)agent premain挂载

运行阶段通过ConfigManager.getConfig(配置类)可以拿到配置。

Sermant源码(一)agent premain挂载

七、BaseOperation初始化

BaseOperation标记接口。

Sermant源码(一)agent premain挂载

OperationManager#initOperations:JDK-SPI通过FrameworkClassLoader加载BaseOperation实现,这表示BaseOperation的实现只能在框架内部的implement.jar中

Sermant源码(一)agent premain挂载

BaseOperation的实现不多,也不太重要,忽略。

八、BaseService初始化

BaseService接口包含启动和停止方法。

Sermant源码(一)agent premain挂载

ServiceManager#initServices:

JDK-SPI使用FrameworkClassLoader加载implement.jar中的BaseService实现类。

如果agent.service.xxx.enabled开启该服务,则放入内存map管理,并调用BaseService#start。

Sermant源码(一)agent premain挂载

这些BaseService接口定义在core中,实现在implement中。

如core定义GatewayClient与backend通讯客户端。

Sermant源码(一)agent premain挂载

implement引入netty依赖,实现NettyGatewayClient

Sermant源码(一)agent premain挂载

运行期间可通过ServiceManager#getService获取服务。

Sermant源码(一)agent premain挂载

如:

Sermant源码(一)agent premain挂载

九、事件系统初始化

EventManager#init:

  1. 注册FrameworkEventCollector(框架核心事件采集)和LogEventCollector(日志事件采集);
  2. 开启定时任务,每隔30s采集所有EventCollector的事件,发送到backend;

Sermant源码(一)agent premain挂载

框架中有N个EventCollector,负责不同的业务。

每个EventCollector有100容量的阻塞队列。

发送数据到EventCollector:如果队列满了,将直接发送至backend;如果未满,则由EventManager定时批量消费队列,发送到backend。

Sermant源码(一)agent premain挂载

十、字节码增强安装

Sermant源码(一)agent premain挂载

1、插件的结构

一般插件分为3个部分:

Sermant源码(一)agent premain挂载Sermant源码(一)agent premain挂载

配置文件

配置文件非必须,会被解析到plugin的PluginConfig类中;

Sermant源码(一)agent premain挂载Sermant源码(一)agent premain挂载

PluginConfig由JDK-SPI加载。

Sermant源码(一)agent premain挂载

运行时可以通过PluginConfigManager获取。

Sermant源码(一)agent premain挂载

插件主模块

plugin,插件主模块,用于定义切点和拦截逻辑,通过provided方式引入core包和宿主依赖。(可以compile引入三方依赖但是不建议)

Sermant源码(一)agent premain挂载

插件定义

PluginDeclarer插件定义,ClassMatcher定义拦截类,InterceptorDeclarer定义拦截方法和拦截Interceptor

Sermant源码(一)agent premain挂载

插件定义会通过JDK-SPI加载。

Sermant源码(一)agent premain挂载

比如SpringCloudMappingRegistryDeclarer拦截springmvc的RequestMapping注册,交给SpringCloudMappingRegistryInterceptor处理。

Sermant源码(一)agent premain挂载

Interceptor拦截器操作ExecuteContext,可以实现多种逻辑:替换入参、跳过实际方法执行、替换出参等等。

Sermant源码(一)agent premain挂载

插件服务接口定义

比如服务可见性插件,plugin声明CollectorService接口,继承PluginService,用于发送数据到backend。

Sermant源码(一)agent premain挂载

运行时plugin通过PluginServiceManager获取CollectorService实现,实现会由service模块提供。

Sermant源码(一)agent premain挂载

插件服务模块

service为plugin服务,非必须,通过provided方式引入core包和plugin包,可以compile引入三方依赖;

Sermant源码(一)agent premain挂载

service实现CollectorService接口,引入fastjson三方依赖对数据进行序列化。

Sermant源码(一)agent premain挂载

插件服务实现同样使用JDK-SPI加载。

Sermant源码(一)agent premain挂载

2、ByteEnhanceManager初始化

ByteEnhanceManager#init:创建BufferedAgentBuilder

Sermant源码(一)agent premain挂载

BufferedAgentBuilder#actions缓存对bytebuddy的AgentBuilder的构造,用于后续回调。

Sermant源码(一)agent premain挂载

Sermant源码(一)agent premain挂载

BufferedAgentBuilder#build:除插件以外的AgentBuilder构造。

比如isOutputEnhancedClasses=true,可以输出增强class文件到enhancedClasses目录,用于排查问题。

Sermant源码(一)agent premain挂载

3、插件初始化

3-1、主流程

插件通过config/plugins.yaml配置:

  1. plugins:静态插件,通过premain方式挂载时安装增强;
  2. dynamicPlugins:动态插件支持动态安装和卸载,通过agentmain方式挂载,其中active插件在挂载时直接安装,passive插件需要通过命令安装;

Sermant源码(一)agent premain挂载

PluginSystemEntrance#initialize:

  1. 读取plugins.yaml配置文件;
  2. PluginManager根据plugin名称,初始化plugin;

Sermant源码(一)agent premain挂载

PluginManager#initPlugins:循环每个插件名安装增强,不会重复安装。

Sermant源码(一)agent premain挂载

PluginManager#executeInit:

根据插件名定位插件目录,针对每个插件创建一个PluginClassLoader,将plugin名称和PluginClassLoader封装为一个Plugin

Sermant源码(一)agent premain挂载

PluginManager#doInitPlugin:插件初始化分为以下几步

  1. 将plugin目录下的jar放入PluginClassLoader
  2. 如果存在service目录,创建ServiceClassLoader,其parent为PluginClassLoader,放入Plugin;
  3. 加载PluginConfig,读取config配置;
  4. 加载PluginService并start;
  5. 将Plugin放入ClassLoaderManager的PluginClassFinder维护;
  6. 如果premain挂载,将plugin定义缓存到BufferedAgentBuilder的actions中;如果agentmain挂载,直接触发bytebuddy的AgentBuilder所有构造,安装active插件增强;

Sermant源码(一)agent premain挂载

3-2、创建PluginClassLoader

ClassLoaderManager#createPluginClassLoader:

PluginClassLoader是URLClassLoader,用于加载当前插件的plugin目录下的jar,父类加载器是SermantClassLoader

Sermant源码(一)agent premain挂载

PluginClassLoader#loadClass:PluginClassLoader破坏双亲委派

  1. 优先尝试自己findClass,这里能加载plugin目录下的class;
  2. 其次尝试双亲委派加载,从SermantClassLoader(core/common)->AppClassLoader->...;
  3. 最终会尝试使用localClassLoader加载;

Sermant源码(一)agent premain挂载

这个localClassLoader是sermant到用户类加载器的桥梁,Interceptor的类加载器是PluginClassLoader,如果需要在Interceptor中加载用户的类,则需要用到localClassLoader。

对比SkyWalking处理方式不同。InterceptorInstanceLoader是agent的ClassLoader到应用ClassLoader的桥梁,Interceptor的类加载器虽然是agent的classLoader,但是agent的classLoader的parent是应用ClassLoader(如LaunchedURLClassLoader)。

PluginClassLoader#getClassFromLocalClassLoader:

  1. 用户可以通过setLocalLoader,放入指定ClassLoader,用于后续类加载;
  2. 如果用户未设置,默认agent.config.useContextLoader=true,可使用线程上下文类加载器加载;
  3. 否则抛出ClassNotFound;

所以,springboot应用在插件Interceptor中可以加载LaunchedURLClassLoader负责的类。

// 线程id -> localClassLoader
private final HashMap<Long, ClassLoader> localLoader = new HashMap<>();
private Class<?> getClassFromLocalClassLoader(String name) {
    // 优先取当前线程,显示塞入的ClassLoader
    ClassLoader loader = localLoader.get(Thread.currentThread().getId());
    // useContextLoader默认true,也可以用线程上下文类加载器
    if (loader == null && useContextLoader/*default true*/) {
        loader = Thread.currentThread().getContextClassLoader();
    }
    Class<?> clazz = null;
    // 排除自己和ServiceClassLoader的情况,防止StackOverFlow
    if (loader != null && !this.equals(loader) && !(loader instanceof ServiceClassLoader)) {
        try {
            clazz = loader.loadClass(name);
        } catch (ClassNotFoundException e) {
            LOGGER.log(Level.FINE, "Load class failed, msg is {0}", e.getMessage());
        }
    }
    return clazz;
}

public void setLocalLoader(ClassLoader loader) {
    localLoader.put(Thread.currentThread().getId(), loader);
}
public void removeLocalLoader() {
    localLoader.remove(Thread.currentThread().getId());
}

3-3、创建ServiceClassLoader

PluginManager#loadServiceLibs:

如果plugin包含service.jar,则创建一个ServiceClassLoader,其parent是当前Plugin的PluginClassLoader

Sermant源码(一)agent premain挂载

ServiceClassLoader#loadClass:

  1. 优先尝试从service目录下加载;
  2. 其次尝试双亲委派 PluginClassLoader->SermantClassLoader->AppClassLoader...

所以ServiceClassLoader处于整个类加载结构的最底层,可以加载到所有类

Sermant源码(一)agent premain挂载

3-4、加载PluginConfig

PluginConfigManager在core包中,管理所有插件配置。

Sermant源码(一)agent premain挂载

PluginConfigManager#loadPluginConfigs:

PluginConfig由JDK-SPI+PluginClassLoader加载,所以需要声明在plugin模块中。

Sermant源码(一)agent premain挂载

3-5、加载PluginService

core模块的BaseService实现在implement包中,由FrameworkClassLoader加载,由ServiceManager管理。

PluginService是一个特殊BaseService

Sermant源码(一)agent premain挂载

如果插件存在service则PluginService由ServiceClassLoader加载,否则由PluginClassLoader加载。

即PluginService可以实现在plugin模块中,也可以实现在service模块中,一个PluginService只能有一种实现(可以通过SpiWeight注解声明优先级,但是框架里还没地方用)。

PluginServicePluginServiceManager管理。

Sermant源码(一)agent premain挂载

3-6、转换Plugin为PluginDescription缓存到ByteEnhanceManager

PluginManager#doInitPlugin:这里分析premain启动,即静态挂载。

Sermant源码(一)agent premain挂载

ByteEnhanceManager#enhanceStaticPlugin:

调用PluginCollector将Plugin转换为PluginDescription缓存到builder。

Sermant源码(一)agent premain挂载

Sermant源码(一)agent premain挂载

PluginCollector#getDescriptions:可通过两种途径得到PluginDescription

  1. 用户实现PluginDeclarer,转换为PluginDescription,属于高级api;
  2. 用户直接实现PluginDescription,框架通过PluginClassLoader+SPI加载得到,属于低级api,一般不用;

Sermant源码(一)agent premain挂载

PluginCollector#combinePluginDeclarers:

  1. 使用PluginClassLoader加载插件下的所有PluginDeclarer
  2. PluginDeclarer按照匹配类型分组,nameCombineMap根据类名精确匹配,combinedList模糊匹配;
  3. 创建AbstractPluginDescription返回;

注意:PluginDeclarersermant启动最后阶段加载,可以通过ServiceManager、PluginServiceManager、ConfigManager都能拿到SPI实现类,虽然一般不会在PluginDeclarer中这么做。

Sermant源码(一)agent premain挂载

PluginCollector#createPluginDescription:

一个Plugin对应一个AbstractPluginDescription,这里的方法将在类加载时被调用后面再看。

Sermant源码(一)agent premain挂载

AbstractPluginDescription组合了bytebuddy的RawMatcherTransformer,即一个插件对应一个匹配器和一个Transformer。

public abstract class AbstractPluginDescription implements ElementMatcher<TypeDescription>, PluginDescription {
    @Override
    public boolean matches(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module,
            Class<?> classBeingRedefined, ProtectionDomain protectionDomain) {
        return matches(typeDescription);
    }
}
public interface PluginDescription extends AgentBuilder.RawMatcher, AgentBuilder.Transformer {
}

4、注册AdviserInterface

AgentCoreEntrance#install:

对于每个artifact都会创建一个AdviserInterface(god)实现,即DefaultAdviser(core),这一步将当前artifact的Adviser加入AdviserScheduler

注:当前Adviser里还是空,没加入任何Interceptor,需要再ByteEnhanceManager#enhance阶段加入。

public static void install(String artifact, Map<String, Object> argsMap, Instrumentation instrumentation,
        boolean isDynamic) throws Exception {
    // 一个artifact对应一个AdviserInterface
    adviserCache = new DefaultAdviser();
    // 类加载器管理 初始化
    ClassLoaderManager.init(argsMap);
     // .. 业务初始化
    // 字节码增强 初始化
    ByteEnhanceManager.init(instrumentation);
    // 插件 初始化
    PluginSystemEntrance.initialize(isDynamic);
    // 注册本次增强的adviser
    AdviserScheduler.registry(adviserCache);
    // premain方式启动,插件增强定义
    if (!isDynamic) {
        ByteEnhanceManager.enhance();
    }
    // ...
}

AdviserScheduler(god)管理了n个artifact的AdviserInterface

public class AdviserScheduler {
    private static final ArrayList<AdviserInterface> ADVISERS = new ArrayList<>();
}

AdviserScheduler#onMethodEnter:运行时用户代码就能通过进入n个artifact的AdviserInterface。

public static ExecuteContext onMethodEnter(Object context, String adviceKey) throws Throwable {
    ExecuteContext executeContext = (ExecuteContext) context;

    // In multi-sermant scenario, method enter is executed in sequence
    for (AdviserInterface currentAdviser : ADVISERS) {
        if (currentAdviser != null) {
            executeContext = currentAdviser.onMethodEnter(executeContext, adviceKey);
        }
    }
    return executeContext;
}

后面会看到,AdviserScheduler在运行时就是用户ClassLoader到agent的桥梁。

Sermant源码(一)agent premain挂载

5、使用bytebuddy api安装增强

ByteEnhanceManager#enhance:

在2.0版本sermant对启动速度进行优化,通过agent.config.preFilter.enable=true开启。

public static void enhance() {
     // 加载unmatched_class_name.txt到内存,用于matcher加速匹配
    cacheUnmatchedClass();
     // 安装增强
    builder.install(instrumentationCache);
    // 注册ShutdownHook,将未匹配class,写入unmatched_class_name.txt
    saveUnMatchedClass(); 
}

优化方式是,将类加载阶段未匹配增强的class名记录到unmatched_class_name.txt本地文件中,下次启动可以直接跳过这些类匹配。

org.yaml.snakeyaml.events.SequenceEndEvent
org.apache.commons.lang3.builder.HashCodeBuilder
javax.servlet.Filter
...

BufferedAgentBuilder#install:

将前面缓存的BuilderAction最终执行,使用butebuddy的AgentBuilder安装到Intrumentation上。

public ResettableClassFileTransformer install(Instrumentation instrumentation) {
    AgentBuilder builder = new Default().disableClassFormatChanges();
    for (BuilderAction action : actions) {
        builder = action.process(builder);
    }
    return builder.installOn(instrumentation);
}

BufferedAgentBuilder#addPlugins:对于插件,将第三步的PluginDescription安装到AgentBuilder中,等类加载触发。

public BufferedAgentBuilder addPlugins(Iterable<PluginDescription> plugins) {
    return addAction(new BuilderAction() {
        @Override
        public AgentBuilder process(AgentBuilder builder) {
            AgentBuilder newBuilder = builder;
            for (PluginDescription plugin : plugins) {
                newBuilder = newBuilder.type(plugin/*RawMatcher*/)
                .transform(plugin/*Transformer*/);
            }
            return newBuilder;
        }
    });
}

十一、类加载

agent启动阶段利用butebuddy将Transformer安装到Instrumentation,类加载阶段回调AbstractPluginDescription。

1、Class匹配

BufferedAgentBuilder#setIgnoredRule:IgnoredMatcher用于忽略class。

private BufferedAgentBuilder setIgnoredRule() {
    return addAction(builder -> builder.ignore(new IgnoredMatcher(config)));
}

BufferedAgentBuilder.IgnoredMatcher#matches:以下class被忽略增强

  1. 2.0启动速度优化,unmatched_class_name.txt记录上次启动未匹配class;
  2. 忽略ServiceClassLoader加载且在agent.config.serviceInjectList配置中的class;
  3. 忽略array和原始类型;
  4. 忽略sermant其他classloader加载的类;
  5. 根据agent.config.ignoredPrefixes忽略类名前缀;
  6. 根据agent.config.ignoredInterfaces忽略接口实现类;
@Override
public boolean matches(TypeDescription typeDesc, ClassLoader classLoader, JavaModule javaModule,
        Class<?> classBeingRedefined, ProtectionDomain protectionDomain) {
    // 2.0优化,unmatched_class_name.txt上次未匹配class,直接跳过
    if (unMatchedClassCache.containsKey(typeDesc.getActualName())) {
        return true;
    }

    // ServiceClassLoader加载的class,在agent.config.serviceInjectList中,跳过
    if (!checkInjectList(typeDesc, classLoader)) {
        return false;
    }

    // array 原始类型 sermant的其他classloader加载的类 
    // ignoredPrefixes忽略配置前缀名称类 
    // ignoredInterfaces忽略接口实现类
    return isArrayOrPrimitive(typeDesc) || checkClassLoader(typeDesc, classLoader)
            || isIgnoredPrefixes(typeDesc) || isIgnoredInterfaces(typeDesc);
}

默认情况下ignoredInterfaces配置了spring的org.springframework.cglib.proxy.Factory,避免了spring创建的动态代理对象被二次增强。

agent.config.ignoredInterfaces=org.springframework.cglib.proxy.Factory

PluginCollector#doMatch:接下来正向匹配哪些class要被增强

  1. 如果PluginDeclarer是类名精确匹配,直接通过nameCombinedMap执行O(1)复杂度匹配;
  2. 反之,回调PluginDeclarer的ClassMatcher对TypeDescription进行匹配;
private static AbstractPluginDescription createPluginDescription(Plugin plugin,
                                                                 Map<String, List<PluginDeclarer>> nameCombinedMap, List<PluginDeclarer> combinedList) {
    return new AbstractPluginDescription() {
        // ... 
        @Override
        public boolean matches(TypeDescription target) {
            final String typeName = target.getActualName();
            doMatch(target, typeName, combinedList, nameCombinedMap);
            return nameCombinedMap.containsKey(typeName);
        }
    };
}

private static void doMatch(TypeDescription target, String typeName, List<PluginDeclarer> combinedList,
                            Map<String, List<PluginDeclarer>> nameCombinedMap) {
    for (PluginDeclarer declarer : combinedList) {
        if (matchTarget(declarer.getClassMatcher(), target)) { // 执行class匹配
            List<PluginDeclarer> declarers = nameCombinedMap.computeIfAbsent(typeName,
                                                                             key -> new ArrayList<>());
            if (!declarers.contains(declarer)) {
                declarers.add(declarer);
            }
        }
    }
}

PluginCollector#matchTarget:记录了未匹配class到内存,待ShutdownHook阶段刷写到unmatched_class_name.txt

private static boolean matchTarget(ElementMatcher<TypeDescription> matcher, TypeDescription target) {
    boolean result = matcher.matches(target);
    // agent.config.preFilter.enable=true 记录未匹配class
    if (IS_PRE_FILTER_ENABLE && !result) {
        FileUtils.addToUnmatchedClassCache(target.getActualName());
    }
    return result;
}

2、transform修改字节码

PluginCollector#doTransform:

  1. 循环class匹配的PluginDeclarer,获取class的方法增强声明InterceptDeclarer集合;
  2. 获取InterceptDeclarer会将用户classloader,比如springboot的LaunchedURLClassLoader放入PluginClassLoader的local临时ClassLoader,所以getInterceptDeclarers中能加载到用户class;
  3. 调用ReentrantTransformer#transform;
private static AbstractPluginDescription createPluginDescription(Plugin plugin,
        Map<String, List<PluginDeclarer>> nameCombinedMap, List<PluginDeclarer> combinedList) {
    return new AbstractPluginDescription() {
        @Override
        public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader,
                JavaModule javaModule, ProtectionDomain protectionDomain) { // 类加载阶段,构建Transform
            return doTransform(builder, typeDescription, classLoader, javaModule, protectionDomain, nameCombinedMap,
                    plugin);
        }
    };
}

private static Builder<?> doTransform(Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader,
        JavaModule javaModule, ProtectionDomain protectionDomain, Map<String, List<PluginDeclarer>> nameCombinedMap,
        Plugin plugin) {
    final List<PluginDeclarer> pluginDeclarers = nameCombinedMap.get(typeDescription.getActualName());
    final List<InterceptDeclarer> interceptDeclarers = new ArrayList<>();
    for (PluginDeclarer pluginDeclarer : pluginDeclarers) {
        ClassLoader loader = pluginDeclarer.getClass().getClassLoader();
        if (loader instanceof PluginClassLoader) {
            PluginClassLoader pluginClassLoader = (PluginClassLoader) loader;
            pluginClassLoader.setLocalLoader(classLoader); // 将原始的class的类加载器,放入local
            interceptDeclarers.addAll(Arrays.asList(
                    pluginDeclarer.getInterceptDeclarers(ClassLoader.getSystemClassLoader())));
            pluginClassLoader.removeLocalLoader();
        } 
        // ...
    }
    return new ReentrantTransformer(interceptDeclarers.toArray(new InterceptDeclarer[0]), plugin)
    .transform(builder, typeDescription, classLoader, javaModule, protectionDomain);
}

AbstractTransformer#transform:允许在匹配class成功的情况下,插件Declarer不返回InterceptDeclarer,则不进行任何方法增强。

public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader,
        JavaModule javaModule, ProtectionDomain protectionDomain) {
    if (interceptDeclarers == null || interceptDeclarers.length == 0) {
        return builder;
    }
    return enhanceMethods(builder, typeDescription, classLoader);
}

AbstractTransformer#enhanceMethods:循环每个非抽象native方法执行增强。

private DynamicType.Builder<?> enhanceMethods(DynamicType.Builder<?> builder, TypeDescription typeDesc,
        ClassLoader classLoader) {
    final MethodList<InDefinedShape> declaredMethods = typeDesc.getDeclaredMethods();
    DynamicType.Builder<?> newBuilder = builder;
    for (MethodDescription.InDefinedShape methodDesc : declaredMethods) {
        if (methodDesc.isNative() || methodDesc.isAbstract()) {
            continue;
        }
        newBuilder = enhanceMethod(newBuilder, methodDesc, classLoader);
    }
    return newBuilder;
}

AbstractTransformer#enhanceMethod:

  1. 对于每个method匹配InterceptDeclarer,获取Interceptor,注意这里传入的classloader是被增强类的classloader,在InterceptDeclarer获取Interceptor时可以使用;
  2. 执行resolve对方法增强,对于不同类型方法走不同模板class,如实例方法走TemplateForMember
private DynamicType.Builder<?> enhanceMethod(DynamicType.Builder<?> builder,
        MethodDescription.InDefinedShape methodDesc, ClassLoader classLoader) {
    // 1. 获取interceptor
    final List<Interceptor> interceptors = getInterceptors(methodDesc, classLoader);
    if (interceptors.isEmpty()) {
        return builder;
    }
    // 2. 方法增强
    try {
        if (methodDesc.isStatic()) {
            return resolve(builder, methodDesc, interceptors, TemplateForStatic.class, classLoader);
        } else if (methodDesc.isConstructor()) {
            return resolve(builder, methodDesc, interceptors, TemplateForCtor.class, classLoader);
        } else {
            return resolve(builder, methodDesc, interceptors, TemplateForMember.class, classLoader);
        }
    } catch (NoSuchFieldException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
        LOGGER.warning(String.format(Locale.ROOT, "Enhance [%s] failed for [%s], caused by [%s]. ",
                MethodKeyCreator.getMethodDescKey(methodDesc), e.getClass().getName(), e.getMessage()));
    }
    return builder;
}
private List<Interceptor> getInterceptors(MethodDescription.InDefinedShape methodDesc, ClassLoader classLoader) {
    final List<Interceptor> interceptors = new ArrayList<>();
    for (InterceptDeclarer declarer : interceptDeclarers) {
        // 1. 匹配方法
        if (!declarer.getMethodMatcher().matches(methodDesc)) {
            continue;
        }
        // 2. 匹配成功,获取Interceptor
        interceptors.addAll(Arrays.asList(declarer.getInterceptors(classLoader)));
    }
    return interceptors;
}

ReentrantTransformer#resolve:

  1. 拼接adviceKey=template类名+用户类+增强方法签名+用户类的classloader;
  2. BaseAdviseHandler(core) 存储当前artifact下每个方法对应的Interceptor集合,这将在运行时用到;
  3. 最后将被增强方法和模板class注入bytebuddy的Builder;
@Override
protected Builder<?> resolve(Builder<?> builder, InDefinedShape methodDesc, List<Interceptor> interceptors,
        Class<?> templateCls, ClassLoader classLoader)
        throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException {
    // 增强key=template类名+用户类+增强方法签名+用户类的classloader
    final String adviceKey = getAdviceKey(templateCls, classLoader, methodDesc);
    // BaseAdviseHandler,artifact级别,增强key下的Interceptor
    List<Interceptor> interceptorsForAdviceKey = BaseAdviseHandler.getInterceptorListMap()
            .computeIfAbsent(adviceKey, key -> new ArrayList<>());
    // plugin级别,增强key下的interceptor的className
    Set<String> createdInterceptorForAdviceKey = plugin.getInterceptors()
            .computeIfAbsent(adviceKey, key -> new HashSet<>());
    for (Interceptor interceptor : interceptors) {
        if (checkInterceptor(adviceKey, interceptor.getClass().getCanonicalName())) {
            interceptorsForAdviceKey.add(interceptor); // 【interceptor加入BaseAdviseHandler】
            createdInterceptorForAdviceKey.add(interceptor.getClass().getCanonicalName());
        }
    }
    if (checkAdviceLock(adviceKey)) {
        // 安装增强
        return builder.visit(Advice.to(templateCls).on(ElementMatchers.is(methodDesc)));
    }
    return builder;
}

public class BaseAdviseHandler {
    private static final Map<String, List<Interceptor>> INTERCEPTOR_LIST_MAP = new ConcurrentHashMap<>();
}

十二、增强方法执行

以实例方法为例,TemplateForMember模板方法如下。

public class TemplateForMember {

    /**
     * The preceding trigger point of method
     *
     * @param cls enhanced class
     * @param obj the object being enhanced
     * @param method the method being enhanced
     * @param methodKey method key, which is used to find template class
     * @param arguments arguments of method
     * @param adviceKey advice class name
     * @param context execute context
     * @param isSkip Whether to skip the main execution of method
     * @return Skip result
     * @throws Throwable execute exception
     *
     */
    @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
    public static boolean onMethodEnter(@Advice.Origin Class<?> cls,
            @Advice.This(typing = Assigner.Typing.DYNAMIC) Object obj,
            @Advice.Origin Method method, @Advice.Origin("#t\##m#s") String methodKey,
            @Advice.AllArguments(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object[] arguments,
            @Advice.Local(value = "_ADVICE_KEY_$SERMANT_LOCAL") String adviceKey,
            @Advice.Local(value = "_EXECUTE_CONTEXT_$SERMANT_LOCAL") Object context,
            @Advice.Local(value = "_IS_SKIP_$SERMANT_LOCAL") Boolean isSkip

    ) throws Throwable {
        adviceKey = "TemplateForMember_" + Integer.toHexString(methodKey.hashCode()) + "_" + cls.getClassLoader();
        context = ExecuteContext.forMemberMethod(obj, method, arguments, null, null);
        context = AdviserScheduler.onMethodEnter(context, adviceKey);
        arguments = ((ExecuteContext) context).getArguments();
        isSkip = ((ExecuteContext) context).isSkip();
        return isSkip;
    }

    /**
     * The post trigger point of method
     *
     * @param result Method execution result
     * @param throwable Method execution exception
     * @param adviceKey advice class name
     * @param context execute context
     * @param isSkip Whether to skip the main execution of method
     * @throws Throwable execute exception
     */
    @Advice.OnMethodExit(onThrowable = Throwable.class)
    public static void onMethodExit(@Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object result,
            @Advice.Thrown(readOnly = false) Throwable throwable,
            @Advice.Local(value = "_ADVICE_KEY_$SERMANT_LOCAL") String adviceKey,
            @Advice.Local(value = "_EXECUTE_CONTEXT_$SERMANT_LOCAL") Object context,
            @Advice.Local(value = "_IS_SKIP_$SERMANT_LOCAL") Boolean isSkip) throws Throwable {
        context = isSkip ? context : ((ExecuteContext) context).afterMethod(result, throwable);
        context = AdviserScheduler.onMethodExit(context, adviceKey);
        result = ((ExecuteContext) context).getResult();
        if (((ExecuteContext) context).isChangeThrowable()) {
            throwable = ((ExecuteContext) context).getThrowable();
        }
    }
}

如果打开debug,输出被增强class如下:

public class UserService {
    public UserService() {
    }

    public UserModel getUser(String var1) {
        String var2;
        ExecuteContext var3;
        Boolean var4;
        UserModel var6;
        Throwable var12;
        label32: {
            var2 = null;
            var3 = null;
            var4 = null;
            var2 = "TemplateForMember_" + Integer.toHexString("com.xxx.common.service.UserService#getUser(java.lang.String)".hashCode()) + "_" + UserService.class.getClassLoader();
            var3 = ExecuteContext.forMemberMethod(this, UserService.class.getMethod("getUser", String.class), new Object[]{var1}, (Map)null, (Map)null);
            var3 = AdviserScheduler.onMethodEnter(var3, var2);
            var1 = (String)((ExecuteContext)var3).getArguments()[0];
            var4 = ((ExecuteContext)var3).isSkip();
            boolean var5 = var4;
            UserModel var10000;
            if (var5) {
                var10000 = null;
            } else {
                String id = var1;

                try {
                    UserModel userModel = new UserModel();
                    userModel.setId(id);
                    var10000 = userModel;
                } catch (Throwable var11) {
                    var12 = var11;
                    var6 = null;
                    break label32;
                }
            }

            var6 = var10000;
            var12 = null;
        }

        var3 = var4 ? var3 : ((ExecuteContext)var3).afterMethod(var6, var12);
        var3 = AdviserScheduler.onMethodExit(var3, var2);
        var6 = (UserModel)((ExecuteContext)var3).getResult();
        if (((ExecuteContext)var3).isChangeThrowable()) {
            var12 = ((ExecuteContext)var3).getThrowable();
        }

        if (var12 != null) {
            throw var12;
        } else {
            return var6;
        }
    }
}
  1. var2=拼接adviceKey,var3=创建ExecuteContext上下文对象;
  2. AdviserScheduler#onMethodEnter,执行方法前置拦截,出参ExecuteContext可以设置skip,跳过目标方法执行;
  3. 执行目标方法,记录方法执行异常,var12;
  4. AdviserScheduler#onMethodExit,执行方法后置拦截,出参ExecuteContext可以设置新出参getResult,也可以设置新异常getThrowable;

这里最重要的是AdviserScheduler如何调度到m个artifact下的n个Interceptor。

AdviserScheduler在god包中,被BootStrapClassLoader加载,是真正的全局单例class。AdviserScheduler即能被agent加载到,用于注册Adviser;也能被用户加载到,用于执行Adviser。

AdviserInterface在god包中,但AdviserInterface的实现DefaultAdviser在core包中,被FrameworkClassLoader加载,是artifact级别单例class。

public class AdviserScheduler {
    private static final ArrayList<AdviserInterface> ADVISERS = new ArrayList<>();

    public static ExecuteContext onMethodEnter(Object context, String adviceKey) throws Throwable {
        ExecuteContext executeContext = (ExecuteContext) context;

        // In multi-sermant scenario, method enter is executed in sequence
        for (AdviserInterface currentAdviser : ADVISERS) {
            if (currentAdviser != null) {
                executeContext = currentAdviser.onMethodEnter(executeContext, adviceKey);
            }
        }
        return executeContext;
    }
}

DefaultAdviser调用BaseAdviseHandler#handleMethodEnter静态方法执行方法前置拦截。

注意这里传入了异常处理回调方法,记录异常,这保证了agent逻辑一般不会影响业务

public class DefaultAdviser implements AdviserInterface {

    private void logError(String scene, ExecuteContext context, Interceptor interceptor, Throwable throwable) {
        LOGGER.log(Level.SEVERE, String.format(Locale.ROOT, "An error occurred %s [%s] in interceptor [%s]: ", scene,
                MethodKeyCreator.getMethodKey(context.getMethod()), interceptor.getClass().getName()), throwable);
    }

    @Override
    public ExecuteContext onMethodEnter(ExecuteContext context, String adviceKey) throws Throwable {
        return BaseAdviseHandler.handleMethodEnter(context, adviceKey, new BaseAdviseHandler.ExceptionHandler() {
            @Override
            public void handle(ExecuteContext context, Interceptor interceptor, Throwable throwable) {
                logError("before executing", context, interceptor, throwable);
            }
        });
    }
}

BaseAdviseHandler#handleMethodEnter:

BaseAdviseHandler在类加载transform阶段,保存了adviceKey对应Interceptor关系,自然可以执行Interceptor逻辑。

public class BaseAdviseHandler {
    private static final Map<String, List<Interceptor>> INTERCEPTOR_LIST_MAP = new ConcurrentHashMap<>();

    public static ExecuteContext handleMethodEnter(ExecuteContext context, String adviceKey,
            ExceptionHandler enterHandler) throws Throwable {
        List<Interceptor> interceptorList = INTERCEPTOR_LIST_MAP.get(adviceKey);
        if (interceptorList == null) {
            return context;
        }
        context.setInterceptorIterator(interceptorList.listIterator());
        return handleMethodEnter(context, context.getInterceptorIterator(), enterHandler);
    }
}

BaseAdviseHandler#handleMethodEnter:循环执行每个Interceptor。

每个Interceptor返回的ExecuteContext会作为下一个Interceptor的入参;

如果ExecuteContext返回skip,直接跳过剩余Interceptor;

如果Interceptor发生异常,被catch并打印日志;

可以设置ExecuteContext#getThrowableOut,在所有Interceptor执行完毕后抛出异常;

public static ExecuteContext handleMethodEnter(ExecuteContext context, ListIterator<Interceptor> interceptorItr,
        ExceptionHandler enterHandler) throws Throwable {
    ExecuteContext newContext = context;
    while (interceptorItr.hasNext()) {
        try {
            final Interceptor interceptor = interceptorItr.next();
            try {
                final ExecuteContext tempContext = interceptor.before(newContext);
                if (tempContext != null) {
                    newContext = tempContext;
                }
                if (newContext.isSkip()) {
                    return newContext;
                }
            } catch (Throwable t) {
                enterHandler.handle(context, interceptor, t);
            }
        } catch (Exception exception) {
            LOGGER.log(Level.SEVERE, "Exception occurs when method enter.", exception);
            return newContext;
        }
        if (newContext.getThrowableOut() != null) {
            throw newContext.getThrowableOut();
        }
    }
    return newContext;
}

总结

1、类加载器

Sermant类加载结构如下:

Sermant源码(一)agent premain挂载

  1. AppClassLoader:agent的类加载器,启动后将god.jar安装到BootStrap顶层类加载器,创建SermantClassLoader反射加载core.jar中的真实启动类AgentCoreEntrance
  2. BootStrapClassLoader:顶层类加载器,加入god包后实现了n个artifact的sermant实例挂载(SermantManager),同时god包也是用户类到agent类的桥梁(AdviserScheduler);
  3. SermantClassLoader:每个artifact对应一个SermantClassLoader,负责加载common和core包,core只依赖god和bytebuddy。如果依赖三方依赖实现能力,需要声明接口,都通过JDK-SPI获取implement实现,如BaseService。SermantClassLoader是sermant的顶层类加载器,与用户classloader(如springboot)隔离,双方加载自己的class不会互相干扰,不会发生类冲突;
  4. FrameworkClassLoader:加载implement包,implement包是sermant框架对于core包中核心服务实现,引入了三方依赖(如netty),与插件类加载器隔离,不会发生类冲突;
  5. PluginClassLoader:每个插件对应一个PluginClassLoader,负责加载插件中的类,通过临时设置的localClassLoader线程上下文classloader可以加载用户class(这是agent到用户class的桥梁),插件之间相互隔离。provided引入core和宿主依赖,定义拦截类、拦截方法、拦截器。需要三方依赖的情况声明PluginService(虽然1.4之后plugin不由appclassloader加载,允许compile依赖三方),通过SPI加载service模块中的实现;
  6. ServiceClassLoader:如果插件需要引入三方依赖实现功能,可引入service模块实现plugin模块声明的PluginService接口,和FrameworkClassLoader职责类似;

2、premain主流程

premain的启动流程有严格顺序。

  1. premain方法

god包安装到BootStrap类加载器

创建artifact对应SermantClassLoader

反射执行AgentCoreEntrance;

  1. 类加载器初始化

ClassLoaderManager.init,将common加入SermantClassLoader,创建FrameworkClassLoader

  1. 业务服务初始化

LoggerFactory-日志:SermantBridgeHandler可以将WARNERROR级别日志,通过事件系统发送到backend;

ConfigManager-配置:JDK-SPI用SermantClassLoader加载core包中的BaseConfig实现,将配置注入BaseConfig;

OperationManager-BaseOperation类:JDK-SPI用FrameworkClassLoader加载BaseOperation实现,BaseOperation实现不多可以忽略;

ServiceManager-BaseService类:JDK-SPI用FrameworkClassLoader加载implement.jar中的BaseService实现,需要通过agent.service.[模块名].enabled配置开启,这里会调用BaseService#start,BaseService比较重要的实现如GatewayClient,实现与backend通讯;

EventManager-事件:EventManager管理N个Collector,每隔30s采集所有Collector缓存队列(100)中的事件,发送给backend;

  1. 字节码增强安装

PluginSystemEntrance.initialize-插件初始化:

1)根据插件配置文件plugin.yaml,创建N个Plugin,每个Plugin对应一个plugin的产物目录;

2)每个Plugin有一个PluginClassLoader加载plugin.jar,如果存在service目录,创建ServiceClassLoader,加载service.jar;

3)JDK-SPI用PluginClassLoader加载PluginConfig实现,注入插件配置;

4)JDK-SPI加载PluginService并start,如果插件存在service则由ServiceClassLoader加载,否则由PluginClassLoader加载;

5)使用PluginClassLoader加载插件下的所有PluginDeclarer,封装为AbstractPluginDescription,缓存到BufferedAgentBuilder

AdviserScheduler-注册本次增强AdviserInterface

1)AdviserScheduler由BootStrapClassLoader加载,静态管理n个artifact下的AdviserInterface

2)AdviserInterface的实现是DefaultAdviserDefaultAdviser通过BaseAdviseHandler管理n个方法下的m个Interceptor;

Sermant源码(一)agent premain挂载

ByteEnhanceManager.enhance-安装增强:将前面缓存的BuilderAction最终执行(如PluginDefinition),使用butebuddy的AgentBuilder安装到Intrumentation上。

3、类加载

1)对被加载类执行class正逆向匹配,得到类对应的PluginDeclarer集合;

2)调用PluginDeclarer的getInterceptDeclarers方法,得到InterceptDeclarer集合;

3)循环类的每个方法,调用InterceptDeclarerInterceptDeclarer#getMethodMatcher匹配方法;

4)方法匹配成功,调用InterceptDeclarer#getInterceptors,获取interceptor,拼接adviceKey注册到BaseAdviseHandler中,供运行时调用;

5)将被增强方法和模板class注入bytebuddy的Builder,执行增强;

4、增强方法执行

由于ExecuteContext、AdviserScheduler都能被BootStrapClassLoader加载,所以用户代码能进入agent的Interceptor逻辑。

被增强的用户方法,会被AdviserScheduler中的Adviser拦截,每个Adviser包含了一个artifact下的n个该adviceKey方法下的Interceptor。

var2 = "TemplateForMember_" + Integer.toHexString("com.xxx.common.service.UserService#getUser(java.lang.String)".hashCode()) + "_" + UserService.class.getClassLoader();
var3 = ExecuteContext.forMemberMethod(this, UserService.class.getMethod("getUser", String.class), new Object[]{var1}, (Map)null, (Map)null);
var3 = AdviserScheduler.onMethodEnter(var3, var2);
var4 = ((ExecuteContext)var3).isSkip();
var1 = (String)((ExecuteContext)var3).getArguments()[0];
if (var4) {
   var6 = null;
} else {
  try {
     var6 = 用户代码逻辑;
  } catch (Throwable e) {
      var12 = e;
      var6 = null;
   }
}
var3 = var4 ? var3 : ((ExecuteContext)var3).afterMethod(var6, var12);
var3 = AdviserScheduler.onMethodExit(var3, var2);
var6 = (UserModel)((ExecuteContext)var3).getResult();
if (((ExecuteContext)var3).isChangeThrowable()) {
    var12 = ((ExecuteContext)var3).getThrowable();
}

if (var12 != null) {
    throw var12;
} else {
    return var6;
}

方法增强逻辑包含:

  1. enter阶段,ExecuteContext#isSkip,跳过用户代码;
  2. enter阶段,ExecuteContext#getArguments,替换方法入参;
  3. exit阶段,ExecuteContext#getResult,替换方法出参;
  4. exit阶段,ExecuteContext#getThrowable,替换方法异常;

此外,如果Interceptor发生异常,DefaultAdviser会记录异常日志,不会阻断流程。

欢迎大家评论或私信讨论问题。

本文原创,未经许可不得转载。

欢迎关注公众号【程序猿阿越】。

转载自:https://juejin.cn/post/7398789000222605351
评论
请登录