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类库的流程相同

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/194504.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python科研绘图:P-P图与Q-Q图

目录 什么是P-P图与Q-Q图 分位数 百分位数 Q-Q图步骤与原理 Shapiro-Wilk检验 绘制Q-Q图 绘制P-P图 什么是P-P图与Q-Q图 P-P图和Q-Q图都是用于检验样本的概率分布是否服从某种理论分布。 P-P图的原理是检验实际累积概率分布与理论累积概率分布是否吻合。若吻合&#xf…

SpringBoot整合Quartz示例

数据表 加不加无所谓,如果需要重启服务器后重新执行所有JOB就把sql加上 如果不加表 将application.properties中的quartz数据库配置去掉 自己执行自己的逻辑来就好,大不了每次启动之后重新加载自己的逻辑 链接&#xff1a;https://pan.baidu.com/s/1KqOPYMfI4eHcEMxt5Bmt…

2023年05月 Python(六级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 明明每天坚持背英语单词,他建立了英语单词错题本文件“mistakes.txt”,将每天记错的单词增加到该文件中,下列打开文件的语句最合适的是?( ) A: f = open(“mistakes.txt”) B: …

yolov5模型代码怎么修改

yaml配置文件 深度乘积因子 宽度乘积因子 所有版本只有这两个参数的不同&#xff0c;s m l x逐渐加宽加深 各种类型层参数对照 backbone里的各层&#xff0c;在这里解析&#xff0c;只需要改.yaml里的各层参数就能控制网络结构 修改网络结构 第一步&#xff1a;把新加的模块…

家庭网络中的组网方式

家庭网络中&#xff0c;目前也衍生出了比较多的组网方式&#xff0c;也不是只有Easymesh&#xff0c;我们还是要辩证的去看&#xff0c;没有绝对的好和坏&#xff0c;需求不同&#xff0c;取舍不同。 这里博主简单的介绍几种组网方式&#xff0c;上图也比较直观 1.wds wds是…

Windows系统中搭建docker (ubuntu,Docker-desktop)

一、docker安装前的准备工作 1. 开启CPU虚拟化&#xff0c;新电脑该默认是开启的&#xff0c;如果没开启可以根据自己电脑型号品牌搜索如克开启CPU虚拟化。当开启成功后可在设备管理器中看到。 2.开通Hyper-V 通过 Windows 控制面板 --> 程序和功能 -->启用或关闭…

Windows 11 设置 wsl-ubuntu 使用桥接网络

Windows 11 设置 wsl-ubuntu 使用桥接网络 0. 背景1. Windows 11 下启用 Hyper-V2. 使用 Hyper-V 虚拟交换机管理器创建虚拟网络3. 创建 .wslconfig 文件4. 配置 wsl.conf 文件5. 配置 wsl-network.conf 文件6. 创建 00-wsl2.yaml7. 安装 net-tools 和 openssh-server 0. 背景 …

Day48 力扣动态规划 : 647. 回文子串 |516.最长回文子序列 |动态规划总结篇

Day48 力扣动态规划 : 647. 回文子串 &#xff5c;516.最长回文子序列 &#xff5c;动态规划总结篇 647. 回文子串第一印象看完题解的思路dp递推公式初始化递归顺序 实现中的困难感悟代码 516.最长回文子序列第一印象我的尝试遇到的问题 看完题解的思路dp递推公式初始化 实现中…

搜维尔科技:业内普遍选择Varjo头显作为医疗VR/AR/XR解决方案

Varjo 的人眼分辨率混合现实和虚拟现实头显将医疗专业人员的注意力和情感投入提升到更高水平。借助逼真的 XR/VR&#xff0c;医疗和保健人员可以为最具挑战性的现实场景做好准备&#xff01; 在虚拟、增强和混合现实中进行最高水平的训练和表现 以逼真的 3D 方式可视化医疗数据…

基于Element-Plus动态配置Menu 菜单栏

文章目录 前言先看效果可兼容多级菜单栏&#xff08;顺便配置多少级&#xff09; 一、新建组件二、使用步骤总结如有启发&#xff0c;可点赞收藏哟~ 前言 菜单栏配置化 图标配置化参考vite动态配置svg图标及其他方式集合 先看效果 可兼容多级菜单栏&#xff08;顺便配置多少级…

系列一、请谈谈你对JVM的理解?Java8的虚拟机有什么更新?

一、请谈谈你对JVM的理解&#xff1f;Java8的虚拟机有什么更新&#xff1f; JVM是Java虚拟机的意思。它是建立在操作系统之上的&#xff0c;由类加载器子系统、本地方法栈、Java栈、程序计数器、方法区、堆、本地方法库、本地方法接口、执行引擎组成。 &#xff08;1&#xff0…

istio安装文档

1、重装命令 istioctl manifest generate --set profiledemo | kubectl delete --ignore-not-foundtrue -f - 2、下载 参考&#xff1a;02、istio部署到k8s中 - 简书 (jianshu.com) 参考 Istio / 入门 curl -L https://istio.io/downloadIstio | ISTIO_VERSION1.20.0 TAR…

春秋云境靶场CVE-2021-41402漏洞复现(任意代码执行漏洞)

文章目录 前言一、CVE-2021-41402描述二、CVE-2021-41402漏洞复现1、信息收集1、方法一弱口令bp爆破2、方法二7kb扫路径&#xff0c;后弱口令爆破 2、找可能可以进行任意php代码执行的地方3、漏洞利用找flag 总结 前言 此文章只用于学习和反思巩固渗透测试知识&#xff0c;禁止…

C#asp.net考试系统+sqlserver

C#asp.net简易考试系统 sqlserver在线考试系统学生登陆 判断学生是否存在 选择课程名 科目 可以进行答题操作&#xff0c;已经考试的课程不能再次答题&#xff0c; 自动根据课程名对应的题库生成试卷界面 加入选项类容 说明文档 运行前附加数据库.mdf&#xff08;或sql生成数…

C51--WiFi模块ESP8266--AT指令

ESP8266 面向物联网应用的&#xff0c;高性价比、高度集成的WiFi MCU 简介&#xff1a; 高度集成&#xff1a; ESP8266EX集成了32位Tensilica 处理器、标准数字外设接口、天线开关、射频balun、功率放大器、底噪放大器、过滤器和电源管理模块&#xff0c;可将所占的PCB空间降…

【 云原生 | K8S 】kubeadm 部署Kubernetes集群

目录 1 环境准备 2 所有节点安装docker 3 所有节点安装kubeadm&#xff0c;kubelet和kubectl 4 部署K8S集群 4.1 查看初始化需要的镜像 4.2 初始化kubeadm 4.3 设定kubectl 4.4 所有节点部署网络插件flannel master&#xff08;2C/4G&#xff0c;cpu核心数要求大于2&am…

Spring Task使用介绍

文章目录 Spring Task介绍cron表达式入门案例Spring Task使用步骤全注解的方式代码开发测试结果 代码仓库 Spring Task 介绍 Spring Task 是Spring框架提供的任务调度工具&#xff0c;可以按照约定的时间自动执行某个代码逻辑。 定位定时任务框架 作用定时自动执行某段Java…

vue3 tsx 项目中使用 Antv/G2 实现多线折线图

Antv/G2 文档 Antv/G2 双折线图 安装 antV-G2 通过 npm 安装 项目中安装 antv/g2 依赖库&#xff1a; npm install antv/g2 --save安装成功&#xff1a; 浏览器引入 可以将脚本下载到本地&#xff0c;也可以直接引入在线资源。 引入在线资源 <!-- 引入在线资源&…

全新的FL studio21.2版支持原生中文FL studio2024官方破解版

FL studio2024官方破解版是一款非常专业的音频编辑制作软件。目前它的版本来到了全新的FL studio21.2版&#xff0c;支持原生中文&#xff0c;全面升级的EQ、母带压线器等功能&#xff0c;让你操作起来更加方便&#xff0c;该版本经过破解处理&#xff0c;用户可永久免费使用&a…

【DevOps】Git 图文详解(一):简介及基础概念

Git 图文详解&#xff08;一&#xff09;&#xff1a;简介及基础概念 1.简介&#xff1a;认识 Git2.基础概念&#xff1a;Git 是干什么的&#xff1f;2.1 概念汇总2.2 工作区 / 暂存区 / 仓库2.3 Git 基本流程2.4 Git 状态 1.简介&#xff1a;认识 Git Git 是当前最先进、最主…