Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spy class是如何在各个业务classloader 中加载的? #292

Open
shidongwa opened this issue Dec 3, 2019 · 1 comment
Open

Spy class是如何在各个业务classloader 中加载的? #292

shidongwa opened this issue Dec 3, 2019 · 1 comment

Comments

@shidongwa
Copy link

请教各位大神:

看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)
            );
        }

    }

}

`

@ljluestc
Copy link

这段代码的目的是在运行时动态地将"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可以监控和拦截目标应用程序中的方法调用。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants