当前位置: 首页 > news >正文

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赋值为插件拦截器全类名
  • 和实例方法插桩的InstMethodsInterintercept()方法相比这里多调用了一个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()方法的作用就是将BootstrapClassLoaderAgentClassLoader能够相同

举例说明:

  • 如果BootstrapClassLoader加载的由InstanceMethodInterTemplate生成的类是org.apache.skywalking.xxx.HttpClientInterceptor_internalAgentClassLoader加载了日志用到的ILog和插件拦截器HttpClientIntercepotr
  • AgentClassLoader的顶层父类加载器为BootstrapClassLoader,根据双亲委派模型,从下往上加载是可以拿到的,但是从上往下加载是拿不到的,BootstrapClassLoader中不能拿到ILogHttpClientIntercepotr,所以需要通过prepare()方法打通BootstrapClassLoaderAgentClassLoader

总结

  • 准备工作,使用对应的Template模板来生成实际的拦截增强,实际类名为xxx_internal
  • prepare()方法
    • BootstrapClassLoaderAgentClassLoader能够相通
    • 获取到日志对象ILog
    • 实例化插件定义的拦截器,用来代替非JDK类库增强中的InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader)
  • 后续的增强逻辑和非JDK类库的流程相同

相关文章:

Skywalking流程分析_9(JDK类库中增强流程)

前言 之前的文章详细介绍了关于非JDK类库的静态方法、构造方法、实例方法的增强拦截流程&#xff0c;本文会详细分析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> 初始化,定义,定义长度&#xff0c;定义长度并且赋值&#xff0c;从数组中获取数据返回元素个数size()判断是否为空empty()返回第一个元素front()返回最后一个数back()删除最后一个数pop_back()插入push_back(x)清空clear()begin()end()使用s…...

资深测试总结,现在软件测试有未来吗?“你“的底气在哪里?

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、为什么会有 “…...

Scalable Exact Inference in Multi-Output Gaussian Processes

Orthogonal Instantaneous Linear Mixing Model TY are m-dimensional summaries&#xff0c;ILMM means ‘Instantaneous Linear Mixing Model’&#xff0c;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. 什么是集合 集合框架&#xff1a;用于存储数据的容器。 集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。 任何集合框架都包含三大块内容&#xff1a; 对外的接口、接口的实现和对集合运算的算 法。 接口&#xff1a;表示集合的抽象数据…...

【LeetCode刷题日志】225.用队列实现栈

&#x1f388;个人主页&#xff1a;库库的里昂 &#x1f390;C/C领域新星创作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏✨收录专栏&#xff1a;LeetCode 刷题日志&#x1f91d;希望作者的文章能对你有所帮助&#xff0c;有不足的地方请在评论区留言指正&#xff0c;…...

【JavaScript】fetch 处理流式数据,实现类 chatgpt 对话

本文只包含最基础的请求后端大佬给得对话接口&#xff0c;大部分模型的传参是差不多的&#xff0c;核心还是如何处理 fetch 获取的流数据 import { defineStore } from pinia; import { ElMessage } from element-plus;type Role system | user | assistant; export interfac…...

收发电子邮件

电子邮件是Internet提供的又一个重要服务项目。早在1987年9月20日&#xff0c;中国首封电子邮件就是从北京经意大利向前联邦德国卡尔斯鲁厄大学发出的&#xff0c;在中国首次实现了与Internet的连接&#xff0c;使中国成为国际互联网大家庭中的一员。现在随着Internet的迅速发展…...

sql13(Leetcode570至少有5名直接下属的经理)

代码&#xff1a; 脑子记不住 语法全靠试.. # 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分钟

测试显示&#xff0c;一个对奥威BI软件不太熟悉的人来开发数据可视化报表&#xff0c;要15分钟&#xff0c;而当这个人去套用数据可视化模板做报表&#xff0c;只需5分钟&#xff01; 数据可视化模板是奥威BI上的一个特色功能板块。用户下载后更新数据源&#xff0c;立即就能获…...

C 语言字符串函数

C 语言字符串函数 在本文中&#xff0c;您将学习使用诸如gets()&#xff0c;puts&#xff0c;strlen()等库函数在C中操作字符串。您将学习从用户那里获取字符串并对该字符串执行操作。 您通常需要根据问题的需要来操作字符串。大多数字符串操作都可以自定义方法完成&#xff…...

nvm安装详细教程(卸载旧的nodejs,安装nvm、node、npm、cnpm、yarn及环境变量配置)

文章目录 一、完全卸载旧的nodejs1、打开系统的控制面板&#xff0c;点击卸载程序&#xff0c;卸载nodejs&#xff08;1&#xff09;打开系统的控制面板&#xff0c;点击程序下的卸载程序&#xff08;2&#xff09;找到node.js&#xff0c;鼠标右击出现下拉框&#xff0c;点卸载…...

详细步骤记录:持续集成Jenkins自动化部署一个Maven项目

Jenkins自动化部署 提示&#xff1a;本教程基于CentOS Linux 7系统下进行 Jenkins的安装 1. 下载安装jdk11 官网下载地址&#xff1a;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 基础语法输入输出语法与引号注释&#xff1a;变量&#xff1a; 数据类型与四则运算数据类型四则运算数据类型的查看type()数据类型的转换int()、int()、float() 流程控制格式化输出循环与遍历逻辑运算符list遍历字典dict遍历 跳出…...

【C刷题】day7

&#x1f3a5; 个人主页&#xff1a;深鱼~&#x1f525;收录专栏&#xff1a;【C】每日一练&#x1f304;欢迎 &#x1f44d;点赞✍评论⭐收藏 一、选择题 1、以下对C语言函数的有关描述中&#xff0c;正确的有【多选】&#xff08; &#xff09; A: 在C语言中&#xff0c;一…...

数据挖掘复盘——apriori

read_csv函数返回的数据类型是Dataframe类型 对于Dataframe类型使用条件表达式 dfdf.loc[df.loc[:,0]2]df: 这是一个DataFrame对象的变量名&#xff0c;表示一个二维的表格型数据结构&#xff0c;类似于电子表格或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的校园失物招领管理系统

项目编号&#xff1a; S 006 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S006&#xff0c;文末获取源码。} 项目编号&#xff1a;S006&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 招领管理模块2.2 寻物管理模块2.3 系…...

requests爬虫IP连接初始化问题及解决方案

问题背景 在使用HTTPS爬虫IP连接时&#xff0c;如果第一次请求是chunked方式&#xff0c;那么HTTPS爬虫IP连接将不会被初始化。这个问题可能会导致403错误&#xff0c;或者在使用HTTPS爬虫IP时出现SSL错误。 解决方案 为了解决这个问题&#xff0c;我们可以在requests库的ada…...

Argo Rollouts结合Service进行Blue-Green部署

删除03 部署04 rootk8s-master01:~/learning-jenkins-cicd/09-argocd-and-rollout/rollout-demos# kubectl delete -f 03-rollouts-with-prometheus-analysis.yaml rootk8s-master01:~/learning-jenkins-cicd/09-argocd-and-rollout/rollout-demos# kubectl apply -f 04-rol…...

mongodb——原理简介,docker单机部署

MongoDB noSQL数据库 特点 数据文件存储格式为 BSON &#xff08;JSON 的扩展&#xff09; &#xff5b;“name”&#xff1a;“joe”&#xff5d;这是 BSON 的例子&#xff0c;其中"name"是键&#xff0c;"joe"是值。键值对组成了 BSON 格式。面向集合…...

ThinkPHP 系列漏洞

目录 2、thinkphp5 sql注入2 3、thinkphp5 sql注入3 4、 thinkphp5 SQL注入4 5、 thinkphp5 sql注入5 6、 thinkphp5 sql注入6 7、thinkphp5 文件包含漏洞 8、ThinkPHP5 RCE 1 9、ThinkPHP5 RCE 2 10、ThinkPHP5 rce3 11、ThinkPHP 5.0.X 反序列化漏洞 12、ThinkPHP…...

系列十、你说你做过JVM调优和参数配置,请问如何盘点JVM系统的默认值?

一、JVM的参数类型 1.1、标配参数 java -versionjava -help 1.2、XX参数 1.2.1、Boolean类型 公式&#xff1a;-XX:或者- 某个属性值 表示开启、-表示关闭 # 是否打印GC收集细节 -XX:PrintGCDetails -XX:-PrintGCDetails# 是否使用串行垃圾收集器 -XX:UseSerialGC -XX:-UseS…...

Java Web——Web开发介绍

什么是Web开发 Web开发是一种创建和维护全球广域网&#xff08;World Wide Web&#xff09;上的网站和应用的技术。全球广域网也称为万维网(www World Wide Web)&#xff0c;是一个能够通过浏览器访问的互联网上的巨大信息库。 Web开发的目标是创建功能齐全、易于使用和安全的…...

Vue 数据监听机制及 Vue 2.0 和 Vue 3.0 的比较

Vue 数据监听机制 在 Vue 中&#xff0c;数据的变化通常是通过数据劫持&#xff08;Data Binding&#xff09;和观察者模式来实现的。当数据发生变化时&#xff0c;Vue 能够自动更新视图。 Vue 2.0 的数据监听 在 Vue 2.0 中&#xff0c;数据监听是通过 Object.defineProper…...

QT多线程项目中子线程无法修改主线程的ui组件

情况描述 今天我创建了一个QT多线程的工程&#xff0c;框架如下。我希望通过指针的方式&#xff0c;让子线程去直接修改主线程的ui组件&#xff0c;但事与愿违。 class ChildThread : public QThread {Q_OBJECT public:ChildThread (MainThread* par):m_Par(par){}; protecte…...

Python 如何实现备忘录设计模式?什么是备忘录设计模式?Python 备忘录设计模式示例代码

什么是备忘录&#xff08;Memento&#xff09;设计模式&#xff1f; 备忘录&#xff08;Memento&#xff09;设计模式是一种行为型设计模式&#xff0c;用于捕获一个对象的内部状态&#xff0c;并在对象之外保存这个状态&#xff0c;以便在需要时恢复对象到先前的状态。这种模…...

LangChain 代理 Agent(学习笔记)

原文&#xff1a;LangChain 代理 Agent(学习笔记) - 尘叶心繁的专栏 - TNBLOG LangChain 代理 Agent(学习笔记) LangChain 代理 Agent(学习笔记) 简介Agent Zero-shot ReActStructured Input ReActOpenAI FunctionsConversationalSelf ask with searchReAct document storePlan…...

实验三 页面置换算法

一. 实验目的&#xff1a; 1、熟悉虚存管理的各种页面淘汰算法 二、实验环境&#xff1a; 硬件环境&#xff1a;计算机一台&#xff0c;局域网环境&#xff1b; 软件环境&#xff1a;Windows XP及以上版本 Professional操作系统平台&#xff0c;Visual C 6.0专业版或企业版…...

Node.js中的Buffer和Stream

Node.js中的Buffer和Stream 计算机只能理解二进制数据&#xff0c;即0和1形式的数据。这些数据的顺序移动称为流。以称为块&#xff08;chunk&#xff09;的破碎部分流式传输数据&#xff1b;计算机一收到数据块就开始处理数据&#xff0c;而不用等待整个数据。 我们这篇文章…...

3.5 Windows驱动开发:应用层与内核层内存映射

在上一篇博文《内核通过PEB得到进程参数》中我们通过使用KeStackAttachProcess附加进程的方式得到了该进程的PEB结构信息&#xff0c;本篇文章同样需要使用进程附加功能&#xff0c;但这次我们将实现一个更加有趣的功能&#xff0c;在某些情况下应用层与内核层需要共享一片内存…...

【小黑送书—第八期】>>别再吐槽大学教材了,来看看这些网友强推的数学神作!

导读&#xff1a;关于大学数学教材的吐槽似乎从来没停止过。有人慨叹&#xff1a;数学教材晦涩难懂。错&#xff01;难懂&#xff0c;起码还可以读懂。数学教材你根本读不懂&#xff1b;也有人说&#xff1a;数学教材简直就是天书。 数学教材有好有坏&#xff0c;这话不假&…...

MatLab的下载、安装与使用(亲测有效)

1、概述 MatLab是由MathWorks公司开发并发布的&#xff0c;支持线性代数、矩阵运算、绘制函数和数据、信号处理、图像处理以及视频处理等功能。广泛用于算法开发、数据可视化、数据分析以及数值计算等。 Matlab 的主要特性包括&#xff1a; 简单易用的语法&#xff0c;使得程…...

无人智能货柜:引领便捷购物新体验

无人智能货柜&#xff1a;引领便捷购物新体验 无人智能货柜利用人工智能技术&#xff0c;将传统货架与电子商务相结合&#xff0c;形成智能销售终端。其采用先拿货后付款的购物模式&#xff0c;用户只需扫码、拿货、关门三个简洁流畅的步骤&#xff0c;极大地提升了消费者的购物…...

4.6 Windows驱动开发:内核遍历进程VAD结构体

在上一篇文章《内核中实现Dump进程转储》中我们实现了ARK工具的转存功能&#xff0c;本篇文章继续以内存为出发点介绍VAD结构&#xff0c;该结构的全程是Virtual Address Descriptor即虚拟地址描述符&#xff0c;VAD是一个AVL自平衡二叉树&#xff0c;树的每一个节点代表一段虚…...

基于世界杯算法优化概率神经网络PNN的分类预测 - 附代码

基于世界杯算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于世界杯算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于世界杯优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络…...

NPM 与 XUI 共存!Nginx Proxy Manager 搭配 X-UI 实现 Vless+WS+TLS 教程!

之前分享过搭建可以与宝塔共存的一个 “魔法” 服务器状态监控应用 ——xui&#xff0c;支持 VmessWSTLS。 最近 Docker 视频出的比较多&#xff0c;前阵子又出现了宝塔国内版存在隐私泄露的问题&#xff0c;很多小伙伴其实都不用宝塔了&#xff0c;那么&#xff0c;在我们现在…...

【网络奇遇记】那年我与计算机网络的浅相知

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. 计算机网络的定义1.1 计算机早期的一个最简单的定义1.2 现阶段计算机网络的一个较好的定义 二. …...

LeetCode26.删除有序数组中的重复项(双指针法)

LeetCode26.删除有序数组中的重复项 1.问题描述2.解题思路3.代码 1.问题描述 给你一个 非严格递增排列 的数组 nums &#xff0c;请你** 原地** 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然…...

原型网络Prototypical Network的python代码逐行解释,新手小白也可学会!!-----系列8

文章目录 前言一、原始代码二、对每一行代码的解释&#xff1a;总结 前言 这是该系列原型网络的最后一段代码及其详细解释&#xff0c;感谢各位的阅读&#xff01; 一、原始代码 if __name__ __main__:##载入数据labels_trainData, labels_testData load_data() # labels_…...

黑马点评回顾 redis实现共享session

文章目录 传统session缺点整体访问流程代码实现生成验证码登录 问题具体思路 传统session缺点 传统单体项目一般是把session存入tomcat&#xff0c;但是每个tomcat中都有一份属于自己的session,假设用户第一次访问第一台tomcat&#xff0c;并且把自己的信息存放到第一台服务器…...

Redis篇---第八篇

系列文章目录 文章目录 系列文章目录前言一、说说 Redis 哈希槽的概念?二、Redis 常见性能问题和解决方案有哪些?三、假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来?前言 前些天发现了一个巨牛的人工智能学习网站…...

Unity使用Visual Studio Code 调试

Unity 使用Visual Studio Code 调试C# PackageManager安装Visual Studio EditorVisual Studio Code安装Unity 插件修改Unity配置调试 PackageManager安装Visual Studio Editor 打开 Window->PackageManger卸载 Visual Studio Code Editor &#xff0c;这个已经被官方废弃安…...

【Linux】进程替换|exec系列函数

文章目录 一、看一看单进程版的进程替换二、进程替换的原理三、多进程版——验证各种程序替换接口exec系列函数execlexeclpexecvexecvp tipsexecleexecve 四、总结 一、看一看单进程版的进程替换 #include<stdio.h> #include<unistd.h> #include<stdlib.h>i…...

Java编程技巧:将图片导出成pdf文件

目录 一、pom依赖二、代码三、测试链接 一、pom依赖 <!-- pdf插件 start --> <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.3</version> </dependency> <dependency…...

二项分布和泊松分布

一、二项分布 1.1 n重伯努利试验 若是二项分布&#xff0c;则必是n重伯努利试验概型。即&#xff1a;每次试验只有两种结果 与 &#xff0c;且在每次试验中A发生的概率相等&#xff0c;即P(A)p&#xff0c;将这种试验独立重复n次&#xff0c;则称这种试验为n重伯努利试验&#…...

【飞控调试】DJIF450机架+Pixhawk6c mini+v1.13.3固件+好盈Platinium 40A电调无人机调试

1 背景 由于使用了一种新的航电设备组合&#xff0c;在调试无人机起飞的时候遇到了之前没有遇到的问题。之前用的飞控&#xff08;Pixhawk 6c&#xff09;和电调&#xff08;Hobbywing X-Rotor 40A&#xff09;&#xff0c;在QGC里按默认参数配置来基本就能平稳飞行&#xff0…...

Android studio配置Flutter开发环境报错问题解决

博主前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住也分享一下给大家 &#x1f449;点击跳转到教程 报错问题截图 报错原因已经给出&#xff1a; You need Java 11 or higher to build your app with this version of G…...