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.mzph.cn/news/150179.shtml

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

相关文章

开源与闭源:大模型时代的技术交融与商业平衡

一、开源和闭源的优劣势比较 1.1 开源 优势&#xff1a; 1.技术共享与吸引人才&#xff1a; 开源促进了技术共享&#xff0c;吸引了全球范围内的人才参与大模型的发展&#xff0c;形成了庞大的开发者社区。 2.推动创新&#xff1a; 开源模式鼓励开发者共同参与&#xff0c;推动…

uni-app:如何配置uni.request请求的超时响应时间(全局+局部)

方法一&#xff1a;全局配置响应时间 一、进入项目的manifest.json的代码视图模块 二、写入代码 "networkTimeout": {"request": 5000 }, 表示现在request请求响应时间最多位5秒 方法二&#xff1a;局部设置响应时间 一、直接在uni.request中写入属性…

微服务下整合knife4j接口文档

前言:本文旨在解决微服务下通过网关访问所用服务的knife4j文档&#xff0c;无需再通过其他服务单独访问 功能模块配置&#xff1a; 1.配置类&#xff1a; 在这个文件中注意下basePackage的扫描路径&#xff0c;修改为对应controller下的路径。 Configuration EnableSwagger…

打造自己的3D模型AI 自动纹理工具

在线工具推荐&#xff1a; 三维数字孪生场景工具 - GLTF/GLB在线编辑器 - Three.js AI自动纹理化开发 - YOLO 虚幻合成数据生成器 - 3D模型在线转换 - 3D模型预览图生成服务 为 3D 模型创建纹理可能比您想象的要容易。虽然注意细节很重要&#xff0c;但有很多方法可以制…

超详细~25考研规划~感恩现在努力的你!!!

25考研规划 俄语&#xff0c;翻译过来叫我爱你 考试时间 第一天 8.30-11.30政治——100分 2.00-5.00英语——100分 第二天 8.30-11.30数学——150分 2.00-5.00专业课——150分 1.什么是25考研 将在2024年12月参加考研&#xff0c;2025年本科毕业&#xff0c;9月读研究…

java基础练习缺少项目?看这篇文章就够了(下)!

公众号&#xff1a;全干开发 。 专注分享简洁但高质量的动图技术文章&#xff01; 回顾 在上节内容中&#xff0c;我们实现了用户开户的功能createAccount public void start(){System.out.println("欢迎您进入到了ATM系统");System.out.println("1、用户登录&…

git基本用法和操作

文章目录 创建版本库方式&#xff1a;Git常用操作命令&#xff1a;远程仓库相关命令分支(branch)操作相关命令版本(tag)操作相关命令子模块(submodule)相关操作命令忽略一些文件、文件夹不提交其他常用命令 创建版本库方式&#xff1a; 创建文件夹 在目录下 右键 Git Bush H…

如何使用$APPEALS法,分析用户期待?

$APPEALS分析法是一种用于分析用户期待和需求的方法&#xff0c;它可以帮助企业全方位多角度地了解客户对产品的期望&#xff0c;有助于企业多维度有侧重地调整市场规划和产品改进策略&#xff0c;帮助企业打造优势产品&#xff0c;提高市场竞争力。 下面是使用$APPEALS分析法来…

键盘控制ROS车运动

键盘控制ROS车运动 上位机 使用pyseria库与stm32单片机进行通信控制 #!/usr/bin/env python # -*- coding: utf-8 -*import sys, select, termios, tty import serialmsg """ ---------------------------w a x ds w : x a : y s : -x …

广西柳州机械异形零部件三维扫描3D抄数全尺寸测绘建模-CASAIM中科广电

一、背景介绍 复杂机械异形零部件具有不规则的形状和复杂的结构&#xff0c;给生产制造带来了很大的检测难度。为了确保零部件的制造质量和精度&#xff0c;需要对零部件进行全面的尺寸检测和分析。 CASAIM三维扫描仪在机械异形零部件全尺寸检测应用可以实现对机械异形零部件…

腾讯云轻量数据库是什么?性能如何?费用价格说明

腾讯云轻量数据库测评&#xff0c;轻量数据库100%兼容MySQL 5.7和8.0&#xff0c;腾讯云提供1C1G20GB、1C1G40GB、1C2G80GB、2C4G120GB、2C8G240GB五种规格轻量数据库&#xff0c;腾讯云百科txybk.com分享腾讯云轻量数据库测评、轻量数据库详细介绍、特性、配置价格和常见问题解…

JVM 调优指南

文章目录 为什么要学 JVM一、JVM 整体布局二、Class 文件规范三、类加载模块四、执行引擎五、GC 垃圾回收1 、JVM内存布局2 、 JVM 有哪些主要的垃圾回收器&#xff1f;3 、分代垃圾回收工作机制 六、对 JVM 进行调优的基础思路七、 GC 情况分析实例 JVM调优指南 -- 楼兰 ​ JV…

口袋参谋:如何通过布局“问大家”,快速提高宝贝转化!

问大家对于中小卖家来说&#xff0c;是非常适合的&#xff0c;因为我们完全可以靠对问大家的布局&#xff0c;提高宝贝的转化率。 问大家的作用 问大家主要是方便买家在购买前&#xff0c;了解商品的一些问题&#xff0c;而作为该不该购买的参考。对于卖家来说&#xff0c;是…

释放机器人潜力,INDEMIND深耕底层技术

市场转暖&#xff0c;但攘外需要同时安内。 市场降温之后&#xff0c;正迎来拐点 疫情之后&#xff0c;经济逐渐下行&#xff0c;服务机器人的“好日子”也随之结束&#xff0c;整个行业都在动荡中经历渡劫。根据TE智库报告显示&#xff0c;从2022年开始&#xff0c;我国服务…

疑似openAI的BUG

Chat gpt 4.0 『最新数据2023年』 Chat gpt 3.5 智商不在线『最近数据2021年9月左右』 发现了疑似openAI的一个bug 通过固定连接访问就可以用chatgpt4.0 4.0版本的费用为20美金一个月 https://chat.openai.com/?modelgpt-4-gizmo

【Linux】C文件系统详解(四)——磁盘的物理和抽象结构

文章目录 磁盘结构磁盘物理结构磁盘的具体物理结构磁盘结构的逻辑抽象 文件系统BootBlockSuperBlockGroupDescriptorTableinode tableDataBlocksinodeBitmapblockBitmaplinux中的inode 和文件名如何理解文件的增删查改删 补充细节1.如果文件误删了,我们该怎么办?2.inode确定分…

服务器端请求伪造(SSRF)

概念 SSRF(Server-Side Request Forgery&#xff0c;服务器端请求伪造) 是一种由攻击者构造形成的由服务端发起请求的一个安全漏洞。一般情况下&#xff0c;SSRF是要攻击目标网站的内部系统。&#xff08;因为内部系统无法从外网访问&#xff0c;所以要把目标网站当做中间人来…

Thinkphp6实现定时任务功能

本文主要介绍命令启动定时任务的功能&#xff0c;按照CRMEB标准版的程序为大家详细的进行实现过程的介绍 首先创建安装Worker&#xff0c;执行composer require topthink/think-worker 安装在config/console.php中定义指令 timer > \crmeb\command\Timer::class 3. 对应图1…

C++ Qt 学习(八):Qt 绘图技术与图形视图

1. 常见 18 种 Qt 绘图技术 1.1 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <memory> #include <QTreeView> #include "CPaintWidget.h"using namespace std;class Widget : public QWidget {Q_OBJECTpublic:Widget…

使用Python实现3D曲线拟合

曲线拟合是数据分析和数学建模领域中广泛使用的技术。它涉及到寻找最接近一组数据点的数学函数的过程。在3D曲线拟合中&#xff0c;该过程被扩展到三维空间&#xff0c;其中的目标是找到最好地表示一组3D数据点的函数。 Python是一种用于科学计算的流行编程语言&#xff0c;它…