V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
biuaxia
V2EX  ›  Java

Java 中什么情况下类会被重载?

  •  
  •   biuaxia · 2023-04-10 19:34:45 +08:00 · 1931 次点击
    这是一个创建于 575 天前的主题,其中的信息可能已经有所发展或是发生改变。

    项目中有一个类 cn.qy.fk.util.PluginStarter, 当 org.hyperic.sigar.Sigar 的对象被 new 出来时, PluginStarter 整个类的方法都会被重载, 完整的代码我都贴在下面, 想问下大佬们我如果要修改方法内容, 应该如何操作呢?


    想要修改方法的类 cn.qy.fk.util.PluginStarter (从 jar 包中反编译得来):

    package cn.qy.fk.util;
    
    @CustomFunction(
        type = 7
    )
    public class PluginStarter {
        public PluginStarter() {
        }
    
        public boolean start() {
            return (boolean)null;
        }
    
        public void end() {
        }
    }
    

    项目运行时 cn.qy.fk.util.PluginStarter 实际的源码为:

    package cn.qy.fk.util;
    
    import cn.qy.fk.util.CustomFunction;
    import cn.qy.fk.util.SystemProperties;
    
    @CustomFunction(type=7)
    public class PluginStarter {
        public boolean start() {
            String error;
            if ((error = SystemProperties.get((String)"error")) != null) {
                System.out.println(error);
                return false;
            }
            System.out.println("Plugin Context is starting ...");
            return true;
        }
    
        public void end() {
            System.out.println("Plugin Context was started!");
        }
    }
    

    注解 cn.qy.fk.util.CustomFunction (从 jar 包中反编译得来):

    package cn.qy.fk.util;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CustomFunction {
        int type();
    }
    

    项目中用到了:

    • asm-9.1.jar
    • asm-attrs-1.4.1.jar
    • javassist-3.25.0-GA.jar
    • sigar.jar

    我通过 IDEA 的断点发现每当代码执行到了下面的 new Sigar(); 时类就被重载了.

    try {
    	Sigar sigar = new Sigar();
            (new PluginStarter()).start();
    }
    

    项目的资源目录还发现了:

    libsigar-amd64-freebsd-6.so
    libsigar-amd64-linux.so
    libsigar-amd64-solaris.so
    libsigar-ia64-hpux-11.sl
    libsigar-ia64-linux.so
    libsigar-pa-hpux-11.sl
    libsigar-ppc64-aix-5.so
    libsigar-ppc64-linux.so
    libsigar-ppc-aix-5.so
    libsigar-ppc-linux.so
    libsigar-s390x-linux.so
    libsigar-sparc64-solaris.so
    libsigar-sparc-solaris.so
    libsigar-universal64-macosx.dylib
    libsigar-universal-macosx.dylib
    libsigar-x86-freebsd-5.so
    libsigar-x86-freebsd-6.so
    libsigar-x86-linux.so
    libsigar-x86-solaris.so
    
    sigar-amd64-winnt.dll
    sigar-x86-winnt.dll
    sigar-x86-winnt.lib
    

    想问下这种是不是被 jni 给重载了呢?如果要改的话怎么入手?

    11 条回复    2023-04-11 21:58:48 +08:00
    kyrieIvring
        1
    kyrieIvring  
       2023-04-10 21:31:58 +08:00
    一 看 classloader 是不是优先加载你这个 class ,如果是 强行改 classloader 的顺序
    二 看这个 class 是不是被编译进各个系统 jar 包里面了,如果是,你要找到对应的 jar 包,修改一下,再把它放回原来的位置
    biuaxia
        2
    biuaxia  
    OP
       2023-04-11 07:37:19 +08:00 via Android
    @kyrieIvring 确定不是 classloader 导致的,利用类加载机制覆盖也没法。
    biuaxia
        3
    biuaxia  
    OP
       2023-04-11 07:38:44 +08:00 via Android
    @kyrieIvring 贴出来的代码都是从三方 jar 包中反编译出来的,然后运行时通过阿里 arthas(可能拼错)运行 jad 得到的源码
    huyangq
        4
    huyangq  
       2023-04-11 09:07:58 +08:00
    不理解,什么叫类会被重载?我就知道函数(或者说方法、method)重载(overload)
    huyangq
        5
    huyangq  
       2023-04-11 09:13:57 +08:00
    仔细又阅读了一下,你的意思是有一个类 PluginStarter ,直接反编译 jar 包,发现改类中的 start()、end()的实现逻辑是 A ,但是当运行之后,如果执行到一行代码:new Sigar()之后,类 PluginStarter 中的 start()、end()的实现逻辑却成了 B 。
    huyangq
        6
    huyangq  
       2023-04-11 09:14:47 +08:00
    如果是这样,结合你的依赖包 asm 猜测是运行时修改了字节码实现的
    biuaxia
        7
    biuaxia  
    OP
       2023-04-11 09:50:31 +08:00
    @huyangq 是的,如果是运行时修改字节码,有什么方式可以实现我想要的修改吗?
    huyangq
        8
    huyangq  
       2023-04-11 09:55:53 +08:00
    你可以先确认是不是改的字节码,既然你说执行 new Sigar()会修改函数实现,你可在执行 new Sigar()之前、之后调用一下 start()、end()用来确定是否真的在 Sigar 构造函数中修改了函数实现,然后,再去看 Sigar 构造函数的实现 看看里头是不是真的又修改函数实现的代码,如果有,那简单啊 执行构造函数之后,再改成你要的函数实现就可以了
    vvtf
        9
    vvtf  
       2023-04-11 09:59:57 +08:00
    看下 java-agent 吧,
    是用 instrumentation 实现的.
    dif
        10
    dif  
       2023-04-11 10:56:49 +08:00
    有个成熟的监控项目 pinpoint 用到了类似的技术,你可以参考下。都是通过 java-agent 实现的。
    Aresxue
        11
    Aresxue  
       2023-04-11 21:58:48 +08:00
    类不会重载,只是会被 retransform ,简而言之就是方法体里面的字节码会被替换掉,JVMTI 提供了这样的机制,一般都是各种 agent 在启动时就会修改指定方法的字节码,不过也有些剑走偏锋直接在框架中用 asm 或者 javasist 修改的,像 HikariCP 就使用了各种字节码的黑魔法,可以看下有没有使用这两个框架的 api 的地方
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1280 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 23:28 · PVG 07:28 · LAX 15:28 · JFK 18:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.