greys-anatomy
greys-anatomy copied to clipboard
Spy class是如何在各个业务classloader 中加载的?
请教各位大神:
看greys-anatomy/core/src/main/java/com/github/ompc/greys/core/advisor/Enhancer.java 中的代码,是把spyClassFromGreysClassLoader中的Spy.class 拷一份到 spyClassFromTargetClassLoader中去加载,前提是spyClassFromTargetClassLoader中找不到Spy.class的话。问题是Spy.class是打包在greys-agent.jar 而不是 grey-core.jar 中。spyClassFromGreysClassLoader实际从grey-core.jar是找不到这个Spy.class的,最终还是通过父classloader加载。这里是bootstrap classloader。既然Spy.class 是通过bootstrap加载了,那么spyClassFromTargetClassLoader走的是父委托方式,都会找到bootstrap中的Spy.class, 也就不需要在spyClassFromTargetClassLoader中分别加载了。这不是没有达到“派遣间谍混入对方的classLoader中”的目的么?
` /* * 派遣间谍混入对方的classLoader中 */ private void spy(final ClassLoader targetClassLoader) throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
// 如果对方是bootstrap就算了
if (null == targetClassLoader) {
return;
}
// Enhancer类只可能从greysClassLoader中加载
// 所以找他要ClassLoader是靠谱的
final ClassLoader greysClassLoader = Enhancer.class.getClassLoader();
final String spyClassName = GaStringUtils.SPY_CLASSNAME;
// 从GreysClassLoader中加载Spy
final Class<?> spyClassFromGreysClassLoader = loadSpyClassFromGreysClassLoader(greysClassLoader, spyClassName);
if (null == spyClassFromGreysClassLoader) {
return;
}
// 从目标ClassLoader中尝试加载或定义ClassLoader
Class<?> spyClassFromTargetClassLoader = null;
try {
// 去目标类加载器中找下是否已经存在间谍
// 如果间谍已经存在就算了
spyClassFromTargetClassLoader = targetClassLoader.loadClass(spyClassName);
logger.info("Spy already in targetClassLoader : " + targetClassLoader);
}
// 看来间谍不存在啊
catch (ClassNotFoundException cnfe) {
try {// 在目标类加载器中混入间谍
spyClassFromTargetClassLoader = defineClass(
targetClassLoader,
spyClassName,
toByteArray(Enhancer.class.getResourceAsStream("/" + spyClassName.replace('.', '/') + ".class"))
);
} catch (InvocationTargetException ite) {
if (ite.getCause() instanceof java.lang.LinkageError) {
// CloudEngine 由于 loadClass 不到,会导致 java.lang.LinkageError: loader (instance of com/alipay/cloudengine/extensions/equinox/KernelAceClassLoader): attempted duplicate class definition for name: "com/taobao/arthas/core/advisor/Spy"
// 这里尝试忽略
logger.debug("resolve #112 issues", ite);
} else {
throw ite;
}
}
}
// 无论从哪里取到spyClass,都需要重新初始化一次
// 用以兼容重新加载的场景
// 当然,这样做会给渲染的过程带来一定的性能开销,不过能简化编码复杂度
finally {
if (null != spyClassFromTargetClassLoader) {
// 初始化间谍
invokeStaticMethod(
spyClassFromTargetClassLoader,
"init",
greysClassLoader,
getField(spyClassFromGreysClassLoader, "ON_BEFORE_METHOD").get(null),
getField(spyClassFromGreysClassLoader, "ON_RETURN_METHOD").get(null),
getField(spyClassFromGreysClassLoader, "ON_THROWS_METHOD").get(null),
getField(spyClassFromGreysClassLoader, "BEFORE_INVOKING_METHOD").get(null),
getField(spyClassFromGreysClassLoader, "AFTER_INVOKING_METHOD").get(null),
getField(spyClassFromGreysClassLoader, "THROW_INVOKING_METHOD").get(null)
);
}
}
}
`
这段代码的目的是在运行时动态地将"Spy"类注入到目标ClassLoader中,以便在目标应用程序中执行特定的监控和拦截操作。该注入的目的是在运行时监控和操纵目标应用程序的方法调用。
以下是代码的关键部分的解释:
首先,代码获取了当前类(Enhancer)的ClassLoader,即greysClassLoader,用于加载Spy类。
然后,它尝试从greysClassLoader中加载Spy类,以便后续在目标ClassLoader中使用。这是为了确保Spy类在greysClassLoader中可用。
接下来,代码检查是否在目标ClassLoader中已经存在Spy类。如果存在,就没有必要重新加载。
如果在目标ClassLoader中没有找到Spy类(ClassNotFoundException),则代码会尝试将Spy类定义到目标ClassLoader中。这是通过读取Spy.class的字节码并使用ClassLoader的defineClass方法完成的。
最后,不管Spy类是否从greysClassLoader还是从目标ClassLoader中加载,都会执行初始化操作,以确保Spy类能够正常工作。
在整个过程中,代码确保Spy类在greysClassLoader中加载,因为Spy类的功能需要使用greys的ClassLoader来注入到目标ClassLoader中,以便在目标应用程序中执行拦截和监控操作。
尽管Spy类可能已经通过bootstrap ClassLoader加载,但在目标ClassLoader中重新加载Spy类是为了确保其在目标应用程序中的可用性,并允许greys在目标应用程序中执行必要的操作。这个过程是动态的,根据需要在运行时进行的。
总之,这段代码的目的是确保Spy类在目标ClassLoader中加载并能够正常工作,以便greys可以监控和拦截目标应用程序中的方法调用。