Skywalking流程分析_9(JDK类库中增强流程)
前言
之前的文章详细介绍了关于非JDK类库的静态方法、构造方法、实例方法的增强拦截流程,本文会详细分析JDK类库中的类是如何被增强拦截的
回到最开始的SkyWalkingAgent#premain
try {/** 里面有个重点逻辑 把一些类注入到Boostrap类加载器中 为了解决Bootstrap类加载器不能访问App类加载器中的内容的问题* */agentBuilder = BootstrapInstrumentBoost.inject(pluginFinder, instrumentation, agentBuilder, edgeClasses);
} catch (Exception e) {LOGGER.error(e, "SkyWalking agent inject bootstrap instrumentation failure. Shutting down.");return;
}
BootstrapInstrumentBoost.inject
这里是将必要的类注入到Bootstrap ClassLoader
public static AgentBuilder inject(PluginFinder pluginFinder, Instrumentation instrumentation,AgentBuilder agentBuilder, JDK9ModuleExporter.EdgeClasses edgeClasses) throws PluginException {//所有要注入到 Bootstrap ClassLoader 里的类Map<String, byte[]> classesTypeMap = new LinkedHashMap<>();//针对于目标类是JDK核心类库的插件,这里根据插件的拦截点的不同(实例方法、静态方法、构造方法)//使用不同的模板(XxxTemplate)来定义新的拦截器的核心处理逻辑,并且将插件本身定义的拦截器的全类名赋值给模板的//TARGET_INTERCEPTOR字段。 最后,这些新的拦截器的核心处理逻辑都会被放入 Bootstrap ClassLoader 中if (!prepareJREInstrumentation(pluginFinder, classesTypeMap)) {return agentBuilder;}if (!prepareJREInstrumentationV2(pluginFinder, classesTypeMap)) {return agentBuilder;}for (String highPriorityClass : HIGH_PRIORITY_CLASSES) {loadHighPriorityClass(classesTypeMap, highPriorityClass);}for (String highPriorityClass : ByteBuddyCoreClasses.CLASSES) {loadHighPriorityClass(classesTypeMap, highPriorityClass);}/*** Prepare to open edge of necessary classes.*/for (String generatedClass : classesTypeMap.keySet()) {edgeClasses.add(generatedClass);}/*** Inject the classes into bootstrap class loader by using Unsafe Strategy.* ByteBuddy adapts the sun.misc.Unsafe and jdk.internal.misc.Unsafe automatically.*//*** 把一些类注入到Boostrap类加载器中 为了解决Bootstrap类加载器不能访问App类加载器中的内容的问题* */ClassInjector.UsingUnsafe.Factory factory = ClassInjector.UsingUnsafe.Factory.resolve(instrumentation);factory.make(null, null).injectRaw(classesTypeMap);agentBuilder = agentBuilder.with(new AgentBuilder.InjectionStrategy.UsingUnsafe.OfFactory(factory));return agentBuilder;
}
BootstrapInstrumentBoost#prepareJREInstrumentation
private static boolean prepareJREInstrumentation(PluginFinder pluginFinder,Map<String, byte[]> classesTypeMap) throws PluginException {TypePool typePool = TypePool.Default.of(BootstrapInstrumentBoost.class.getClassLoader());// 所有要对JDK核心类库生效的插件List<AbstractClassEnhancePluginDefine> bootstrapClassMatchDefines = pluginFinder.getBootstrapClassMatchDefine();for (AbstractClassEnhancePluginDefine define : bootstrapClassMatchDefines) {// 是否定义实例方法拦截点if (Objects.nonNull(define.getInstanceMethodsInterceptPoints())) {for (InstanceMethodsInterceptPoint point : define.getInstanceMethodsInterceptPoints()) {if (point.isOverrideArgs()) {generateDelegator(classesTypeMap, typePool, INSTANCE_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE, point.getMethodsInterceptor());} else {generateDelegator(classesTypeMap, typePool, INSTANCE_METHOD_DELEGATE_TEMPLATE, point.getMethodsInterceptor());}}}// 是否定义构造器拦截点if (Objects.nonNull(define.getConstructorsInterceptPoints())) {for (ConstructorInterceptPoint point : define.getConstructorsInterceptPoints()) {generateDelegator(classesTypeMap, typePool, CONSTRUCTOR_DELEGATE_TEMPLATE, point.getConstructorInterceptor());}}// 是否定义静态方法拦截点if (Objects.nonNull(define.getStaticMethodsInterceptPoints())) {for (StaticMethodsInterceptPoint point : define.getStaticMethodsInterceptPoints()) {if (point.isOverrideArgs()) {generateDelegator(classesTypeMap, typePool, STATIC_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE, point.getMethodsInterceptor());} else {generateDelegator(classesTypeMap, typePool, STATIC_METHOD_DELEGATE_TEMPLATE, point.getMethodsInterceptor());}}}}return bootstrapClassMatchDefines.size() > 0;
}
总结
- 在之前
PluginFinder对象初始化时就会将加载的所有插件做分类,要对JDK核心类库生效的插件都放入到bootstrapClassMatchDefine集合中,然后调用getBootstrapClassMatchDefine()就是拿到所有要对JDK核心类库生效的插件 - 循环所有要对JDK核心类库生效的插件,分别判断是否定义实例方法拦截点、是否定义构造器拦截点、是否定义静态方法拦截点
下面我们来进入实例方法的增强,并不重写原方法为例来分析详细的流程
private static String INSTANCE_METHOD_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.InstanceMethodInterTemplate";generateDelegator(classesTypeMap, typePool, INSTANCE_METHOD_DELEGATE_TEMPLATE, point.getMethodsInterceptor());
- 这里会调用
generateDelegator方法生成一个代理器 - 其中的参数
INSTANCE_METHOD_DELEGATE_TEMPLATE是一个模版类名来传入 - 模版名为
org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.InstanceMethodInterTemplate,这个是模板不是作为实际的类和对象来调用的
InstanceMethodInterTemplate模板
/*** --------CLASS TEMPLATE---------* <p>Author, Wu Sheng </p>* <p>Comment, don't change this unless you are 100% sure the agent core mechanism for bootstrap class* instrumentation.</p>* <p>Date, 24th July 2019</p>* -------------------------------* <p>* This class wouldn't be loaded in real env. This is a class template for dynamic class generation.* * 这个类是不会被加载的,这是一个类模板用于动态类生成*/
public class InstanceMethodInterTemplate {/*** This field is never set in the template, but has value in the runtime.*/private static String TARGET_INTERCEPTOR;private static InstanceMethodsAroundInterceptor INTERCEPTOR;private static IBootstrapLog LOGGER;/*** Intercept the target instance method.** @param obj target class instance.* @param allArguments all method arguments* @param method method description.* @param zuper the origin call ref.* @return the return value of target instance method.* @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a* bug, if anything triggers this condition ).*/@RuntimeTypepublic static Object intercept(@This Object obj, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper,@Origin Method method) throws Throwable {EnhancedInstance targetObject = (EnhancedInstance) obj;prepare();MethodInterceptResult result = new MethodInterceptResult();try {if (INTERCEPTOR != null) {INTERCEPTOR.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);}} catch (Throwable t) {if (LOGGER != null) {LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());}}Object ret = null;try {if (!result.isContinue()) {ret = result._ret();} else {ret = zuper.call();}} catch (Throwable t) {try {if (INTERCEPTOR != null) {INTERCEPTOR.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t);}} catch (Throwable t2) {if (LOGGER != null) {LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());}}throw t;} finally {try {if (INTERCEPTOR != null) {ret = INTERCEPTOR.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);}} catch (Throwable t) {if (LOGGER != null) {LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());}}}return ret;}/*** Prepare the context. Link to the agent core in AppClassLoader.* - 让BootstrapClassLoader 和 AgentClassloader能够相通* - 获取到ILog生成日志对象* - 获取到插件自定义的拦截器增强类实例* - 替代非JDK核心类库插件运行逻辑里的interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader); */private static void prepare() {if (INTERCEPTOR == null) {ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();if (loader != null) {IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);if (logger != null) {LOGGER = logger;INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);}} else {LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);}}}
}
能看出这个模板类的intercept()方法和实例方法中非jdk类库增强的intercept()方法里面的逻辑都大致差不多
下面来详细分析generateDelegator方法
generateDelegator
/*** Generate the delegator class based on given template class. This is preparation stage level code generation.* 根据给定的模板类生成代理器类,这是准备阶段级别的代码生成* <p>* One key step to avoid class confliction between AppClassLoader and BootstrapClassLoader* 避免AppClassLoader和BootstrapClassLoader之间的类冲突的一个关键步骤** @param classesTypeMap hosts injected binary of generated class 。要注入到Bootstrap类加载器中的类和字节码的映射* key:全类名 value:字节码* @param typePool to generate new class 。加载BootstrapInstrumentBoost的ClassLoader的类型池* @param templateClassName represents the class as template in this generation process. The templates are* pre-defined in SkyWalking agent core。 要进行增强的模板,里面就是环绕增强逻辑的模板* * @param methodsInterceptor 要进行增强逻辑的拦截增强类 */
private static void generateDelegator(Map<String, byte[]> classesTypeMap, TypePool typePool,String templateClassName, String methodsInterceptor) {//要进行具体增强逻辑的拦截增强类名 + _internalString internalInterceptorName = internalDelegate(methodsInterceptor);try {//某个类加载器 已经加载了10个类,但这个类加载器的classpath下有400个类,//typePool.describe可以获取到这个类加载器的classpath下还没有加载类的描述信息TypeDescription templateTypeDescription = typePool.describe(templateClassName).resolve();//把模板通过ByteBuddy编译成字节码DynamicType.Unloaded interceptorType = new ByteBuddy().redefine(templateTypeDescription, ClassFileLocator.ForClassLoader.of(BootstrapInstrumentBoost.class.getClassLoader())).name(internalInterceptorName).field(named("TARGET_INTERCEPTOR")).value(methodsInterceptor).make();classesTypeMap.put(internalInterceptorName, interceptorType.getBytes());InstrumentDebuggingClass.INSTANCE.log(interceptorType);} catch (Exception e) {throw new PluginException("Generate Dynamic plugin failure", e);}
}
- 此方法是将模板类交给ByteBuddy去编译成字节码,改了新的类名,并将TARGET_INTERCEPTOR属性赋值为插件拦截器全类名,然后就放入到classesTypeMap中
- 此Map是要注入到Bootstrap类加载器中的类和字节码的映射,key:全类名 value:字节码
接下来回到BootstrapInstrumentBoost.inject(pluginFinder, instrumentation, agentBuilder, edgeClasses)方法,继续分析剩下的部分
ClassInjector.UsingUnsafe.Factory.resolve(instrumentation)
/*** 把一些必要的类注入到Boostrap类加载器中 为了解决Bootstrap类加载器不能访问App类加载器中的内容的问题* */
ClassInjector.UsingUnsafe.Factory factory = ClassInjector.UsingUnsafe.Factory.resolve(instrumentation);
factory.make(null, null).injectRaw(classesTypeMap);
agentBuilder = agentBuilder.with(new AgentBuilder.InjectionStrategy.UsingUnsafe.OfFactory(factory));
将之前生成模版的方法中,类和字节码的映射的Mapinstrumentation,注入到Bootstrap ClassLoader,此步骤是使用模版进行字节码增强的前提
JDK类库方法的增强过程
想让我们回到ClassEnhancePluginDefine#enhanceInstance,再看一下实例方法的在进行增强是的判断逻辑
if (existedConstructorInterceptPoint) {//获取所有构造方法的切点for (ConstructorInterceptPoint constructorInterceptPoint : constructorInterceptPoints) {//要增加的类是Bootstrap类加载器加载的if (isBootstrapInstrumentation()) {newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher()).intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration().to(BootstrapInstrumentBoost.forInternalDelegateClass(constructorInterceptPoint.getConstructorInterceptor()))));} else {newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher()).intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration().to(new ConstructorInter(constructorInterceptPoint.getConstructorInterceptor(), classLoader))));}}
}/*** 3. enhance instance methods*/
if (existedMethodsInterceptPoints) {//获取所有实例方法的切点for (InstanceMethodsInterceptPoint instanceMethodsInterceptPoint : instanceMethodsInterceptPoints) {String interceptor = instanceMethodsInterceptPoint.getMethodsInterceptor();if (StringUtil.isEmpty(interceptor)) {throw new EnhanceException("no InstanceMethodsAroundInterceptor define to enhance class " + enhanceOriginClassName);}ElementMatcher.Junction<MethodDescription> junction = not(isStatic()).and(instanceMethodsInterceptPoint.getMethodsMatcher());if (instanceMethodsInterceptPoint instanceof DeclaredInstanceMethodsInterceptPoint) {//拦截到的方法必须是当前类上的 注解匹配到的方法有可能不是当前类上的junction = junction.and(ElementMatchers.<MethodDescription>isDeclaredBy(typeDescription));}if (instanceMethodsInterceptPoint.isOverrideArgs()) {//要增加的类是Bootstrap类加载器加载的if (isBootstrapInstrumentation()) {newClassBuilder = newClassBuilder.method(junction).intercept(MethodDelegation.withDefaultConfiguration().withBinders(Morph.Binder.install(OverrideCallable.class)).to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));} else {newClassBuilder = newClassBuilder.method(junction).intercept(MethodDelegation.withDefaultConfiguration().withBinders(Morph.Binder.install(OverrideCallable.class)).to(new InstMethodsInterWithOverrideArgs(interceptor, classLoader)));}} else {//要增加的类是Bootstrap类加载器加载的if (isBootstrapInstrumentation()) {newClassBuilder = newClassBuilder.method(junction).intercept(MethodDelegation.withDefaultConfiguration().to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));} else {newClassBuilder = newClassBuilder.method(junction).intercept(MethodDelegation.withDefaultConfiguration().to(new InstMethodsInter(interceptor, classLoader)));}}}
}
能看到,如果是要增强的类是JDK类库中的,都是调用到BootstrapInstrumentBoost.forInternalDelegateClass的方法
BootstrapInstrumentBoost.forInternalDelegateClass
public static Class forInternalDelegateClass(String methodsInterceptor) {try {return Class.forName(internalDelegate(methodsInterceptor));} catch (ClassNotFoundException e) {throw new PluginException(e.getMessage(), e);}
}public static String internalDelegate(String methodsInterceptor) {return methodsInterceptor + "_internal";
}
通过Class.forName()加载插件拦截器全类名+_internal的类,这个类在Agent启动流程根据模板类生成并注入到Bootstrap ClassLoader中,所以这里是能加载到,再看一下模版InstanceMethodInterTemplate
/*** --------CLASS TEMPLATE---------* <p>Author, Wu Sheng </p>* <p>Comment, don't change this unless you are 100% sure the agent core mechanism for bootstrap class* instrumentation.</p>* <p>Date, 24th July 2019</p>* -------------------------------* <p>* This class wouldn't be loaded in real env. This is a class template for dynamic class generation.* * 这个类是不会被加载的,这是一个类模板用于动态类生成*/
public class InstanceMethodInterTemplate {/*** This field is never set in the template, but has value in the runtime.*/private static String TARGET_INTERCEPTOR;private static InstanceMethodsAroundInterceptor INTERCEPTOR;private static IBootstrapLog LOGGER;/*** Intercept the target instance method.** @param obj target class instance.* @param allArguments all method arguments* @param method method description.* @param zuper the origin call ref.* @return the return value of target instance method.* @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a* bug, if anything triggers this condition ).*/@RuntimeTypepublic static Object intercept(@This Object obj, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper,@Origin Method method) throws Throwable {EnhancedInstance targetObject = (EnhancedInstance) obj;prepare();MethodInterceptResult result = new MethodInterceptResult();try {if (INTERCEPTOR != null) {INTERCEPTOR.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);}} catch (Throwable t) {if (LOGGER != null) {LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());}}Object ret = null;try {if (!result.isContinue()) {ret = result._ret();} else {ret = zuper.call();}} catch (Throwable t) {try {if (INTERCEPTOR != null) {INTERCEPTOR.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t);}} catch (Throwable t2) {if (LOGGER != null) {LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());}}throw t;} finally {try {if (INTERCEPTOR != null) {ret = INTERCEPTOR.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);}} catch (Throwable t) {if (LOGGER != null) {LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());}}}return ret;}/*** Prepare the context. Link to the agent core in AppClassLoader.* - 让BootstrapClassLoader 和 AgentClassloader能够相通* - 获取到ILog生成日志对象* - 获取到插件自定义的拦截器增强类实例* - 替代非JDK核心类库插件运行逻辑里的interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader); */private static void prepare() {if (INTERCEPTOR == null) {ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();if (loader != null) {IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);if (logger != null) {LOGGER = logger;INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);}} else {LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);}}}
}
- JDK类库的类做增强会交给模板
InstanceMethodInterTemplate生成的类来处理,实际是模板中的TARGET_INTERCEPTOR赋值为插件拦截器全类名 - 和实例方法插桩的
InstMethodsInter的intercept()方法相比这里多调用了一个prepare()方法 prepare()方法是让BootstrapClassLoader能够和AgentClassLoader相同的关键
prepare()
/*** Prepare the context. Link to the agent core in AppClassLoader.* - 让BootstrapClassLoader 和 AgentClassloader能够相通* - 获取到ILog生成日志对象* - 获取到插件自定义的拦截器增强类实例* - 替代非JDK核心类库插件运行逻辑里的interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader); */
private static void prepare() {if (INTERCEPTOR == null) {ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();if (loader != null) {IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);if (logger != null) {LOGGER = logger;INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);}} else {LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);}}
}
BootstrapInterRuntimeAssist
public class BootstrapInterRuntimeAssist {private static final String AGENT_CLASSLOADER_DEFAULT = "org.apache.skywalking.apm.agent.core.plugin.loader.AgentClassLoader";private static final String DEFAULT_AGENT_CLASSLOADER_INSTANCE = "DEFAULT_LOADER";private static final String LOG_MANAGER_CLASS = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.BootstrapPluginLogBridge";private static final String LOG_MANAGER_GET_LOGGER_METHOD = "getLogger";private static final PrintStream OUT = System.out;public static ClassLoader getAgentClassLoader() {try {ClassLoader loader = Thread.currentThread().getContextClassLoader();if (loader == null) {return null;}Class<?> agentClassLoaderClass = Class.forName(AGENT_CLASSLOADER_DEFAULT, true, loader);Field defaultLoaderField = agentClassLoaderClass.getDeclaredField(DEFAULT_AGENT_CLASSLOADER_INSTANCE);defaultLoaderField.setAccessible(true);ClassLoader defaultAgentClassLoader = (ClassLoader) defaultLoaderField.get(null);return defaultAgentClassLoader;} catch (Exception e) {e.printStackTrace(OUT);return null;}}public static IBootstrapLog getLogger(ClassLoader defaultAgentClassLoader, String interceptor) {try {Class<?> logManagerClass = Class.forName(LOG_MANAGER_CLASS, true, defaultAgentClassLoader);Method getLogger = logManagerClass.getMethod(LOG_MANAGER_GET_LOGGER_METHOD, String.class);return (IBootstrapLog) getLogger.invoke(null, interceptor + "_internal");} catch (Exception e) {e.printStackTrace(OUT);return null;}}public static <T> T createInterceptor(ClassLoader defaultAgentClassLoader, String className, IBootstrapLog log) {try {Class<?> interceptor = Class.forName(className, true, defaultAgentClassLoader);return (T) interceptor.newInstance();} catch (Exception e) {log.error(e, "Interceptor[{}] not found", className);}return null;}
}
prepare()总结
- 获取到
AgentClassLoader - 用
AgentClassLoader加载桥接器BootstrapPluginLogBridge类,然后调用getLogger方法传入TARGET_INTERCEPTOR+_internal得到这个传入的实际类名的BootstrapPluginLogBridge的对象 - 将得到的
BootstrapPluginLogBridge赋给模板类的成员属性LOGGER - 用
AgentClassLoader加载插件自定义的拦截器实例
为什么要用prepare()来特意的利用桥接来绕一下呢
因为InstanceMethodInterTemplate生成的类是由BootstrapClassLoader去加载的,而日志对象和插件自定义的拦截器都是通过AgentClassLoader去加载的,prepare()方法的作用就是将BootstrapClassLoader和AgentClassLoader能够相同

举例说明:
- 如果
BootstrapClassLoader加载的由InstanceMethodInterTemplate生成的类是org.apache.skywalking.xxx.HttpClientInterceptor_internal,AgentClassLoader加载了日志用到的ILog和插件拦截器HttpClientIntercepotr AgentClassLoader的顶层父类加载器为BootstrapClassLoader,根据双亲委派模型,从下往上加载是可以拿到的,但是从上往下加载是拿不到的,BootstrapClassLoader中不能拿到ILog和HttpClientIntercepotr,所以需要通过prepare()方法打通BootstrapClassLoader和AgentClassLoader
总结
- 准备工作,使用对应的Template模板来生成实际的拦截增强,实际类名为xxx_internal
- prepare()方法
- 将
BootstrapClassLoader和AgentClassLoader能够相通 - 获取到日志对象ILog
- 实例化插件定义的拦截器,用来代替非JDK类库增强中的
InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader)
- 将
- 后续的增强逻辑和非JDK类库的流程相同
相关文章:
Skywalking流程分析_9(JDK类库中增强流程)
前言 之前的文章详细介绍了关于非JDK类库的静态方法、构造方法、实例方法的增强拦截流程,本文会详细分析JDK类库中的类是如何被增强拦截的 回到最开始的SkyWalkingAgent#premain try {/** 里面有个重点逻辑 把一些类注入到Boostrap类加载器中 为了解决Bootstrap类…...
矩阵的QR分解
矩阵的QR分解 GramSchmidt 设存在 B { x 1 , x 2 , … , x n } \mathcal{B}\left\{\mathbf{x}_{1},\mathbf{x}_{2},\ldots,\mathbf{x}_{n}\right\} B{x1,x2,…,xn}在施密特正交化过程中 q 1 x 1 ∣ ∣ x 1 ∣ ∣ q_1\frac{x_1}{||x_1||} q1∣∣x1∣∣x1 q k …...
STL总结
STL vector 头文件<vector> 初始化,定义,定义长度,定义长度并且赋值,从数组中获取数据返回元素个数size()判断是否为空empty()返回第一个元素front()返回最后一个数back()删除最后一个数pop_back()插入push_back(x)清空clear()begin()end()使用s…...
资深测试总结,现在软件测试有未来吗?“你“的底气在哪里?
目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 1、为什么会有 “…...
Scalable Exact Inference in Multi-Output Gaussian Processes
Orthogonal Instantaneous Linear Mixing Model TY are m-dimensional summaries,ILMM means ‘Instantaneous Linear Mixing Model’,OILMM means ‘Orthogonal Instantaneous Linear Mixing Model’ 辅助信息 作者未提供代码...
sqli-labs(Less-3)
1. 通过构造id1’ 和id1’) 和id1’)–确定存在注入 可知原始url为 id(‘1’) 2.使用order by 语句猜字段数 http://127.0.0.1/sqlilabs/Less-3/?id1) order by 4 -- http://127.0.0.1/sqlilabs/Less-3/?id1) order by 3 --3. 使用联合查询union select http://127.0.0.1…...
集合框架面试题
一、集合容器的概述 1. 什么是集合 集合框架:用于存储数据的容器。 集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。 任何集合框架都包含三大块内容: 对外的接口、接口的实现和对集合运算的算 法。 接口:表示集合的抽象数据…...
【LeetCode刷题日志】225.用队列实现栈
🎈个人主页:库库的里昂 🎐C/C领域新星创作者 🎉欢迎 👍点赞✍评论⭐收藏✨收录专栏:LeetCode 刷题日志🤝希望作者的文章能对你有所帮助,有不足的地方请在评论区留言指正,…...
【JavaScript】fetch 处理流式数据,实现类 chatgpt 对话
本文只包含最基础的请求后端大佬给得对话接口,大部分模型的传参是差不多的,核心还是如何处理 fetch 获取的流数据 import { defineStore } from pinia; import { ElMessage } from element-plus;type Role system | user | assistant; export interfac…...
收发电子邮件
电子邮件是Internet提供的又一个重要服务项目。早在1987年9月20日,中国首封电子邮件就是从北京经意大利向前联邦德国卡尔斯鲁厄大学发出的,在中国首次实现了与Internet的连接,使中国成为国际互联网大家庭中的一员。现在随着Internet的迅速发展…...
sql13(Leetcode570至少有5名直接下属的经理)
代码: 脑子记不住 语法全靠试.. # Write your MySQL query statement below select b.name from (select managerId,count(managerId) as numfrom Employeegroup by managerId ) a left join Employee b on a.managerIdb.id where a.num>5 and b.name is not N…...
15分钟,不,用模板做数据可视化只需5分钟
测试显示,一个对奥威BI软件不太熟悉的人来开发数据可视化报表,要15分钟,而当这个人去套用数据可视化模板做报表,只需5分钟! 数据可视化模板是奥威BI上的一个特色功能板块。用户下载后更新数据源,立即就能获…...
C 语言字符串函数
C 语言字符串函数 在本文中,您将学习使用诸如gets(),puts,strlen()等库函数在C中操作字符串。您将学习从用户那里获取字符串并对该字符串执行操作。 您通常需要根据问题的需要来操作字符串。大多数字符串操作都可以自定义方法完成ÿ…...
nvm安装详细教程(卸载旧的nodejs,安装nvm、node、npm、cnpm、yarn及环境变量配置)
文章目录 一、完全卸载旧的nodejs1、打开系统的控制面板,点击卸载程序,卸载nodejs(1)打开系统的控制面板,点击程序下的卸载程序(2)找到node.js,鼠标右击出现下拉框,点卸载…...
详细步骤记录:持续集成Jenkins自动化部署一个Maven项目
Jenkins自动化部署 提示:本教程基于CentOS Linux 7系统下进行 Jenkins的安装 1. 下载安装jdk11 官网下载地址:https://www.oracle.com/cn/java/technologies/javase/jdk11-archive-downloads.html 本文档教程选择的是jdk-11.0.20_linux-x64_bin.tar.g…...
Python学习(一)基础语法
文章目录 1. 入门1.1 解释器的作用1.2 下载1.3 基础语法输入输出语法与引号注释:变量: 数据类型与四则运算数据类型四则运算数据类型的查看type()数据类型的转换int()、int()、float() 流程控制格式化输出循环与遍历逻辑运算符list遍历字典dict遍历 跳出…...
【C刷题】day7
🎥 个人主页:深鱼~🔥收录专栏:【C】每日一练🌄欢迎 👍点赞✍评论⭐收藏 一、选择题 1、以下对C语言函数的有关描述中,正确的有【多选】( ) A: 在C语言中,一…...
数据挖掘复盘——apriori
read_csv函数返回的数据类型是Dataframe类型 对于Dataframe类型使用条件表达式 dfdf.loc[df.loc[:,0]2]df: 这是一个DataFrame对象的变量名,表示一个二维的表格型数据结构,类似于电子表格或SQL表。 df.loc[:, 0]: 这是使用DataFrame的.loc属性来进行…...
Windows10下Maven3.9.5安装教程
文章目录 1.下载maven2.安装3.配置系统变量3.1.新建系统变量 MAVEN_HOME3.2.编辑系统变量Path 4.CMD命令测试是否安装成功5.配置maven本地仓库6.配置国内镜像仓库 1.下载maven 官网 https://maven.apache.org/download.cgi 点击下载。 2.安装 解压到指定目录 D:\installSoft…...
【开源】基于JAVA的校园失物招领管理系统
项目编号: S 006 ,文末获取源码。 \color{red}{项目编号:S006,文末获取源码。} 项目编号:S006,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 招领管理模块2.2 寻物管理模块2.3 系…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
