gpt4 book ai didi

java - 如何在运行时重新转换类

转载 作者:行者123 更新时间:2023-12-02 06:11:34 25 4
gpt4 key购买 nike

我正在修改已经加载到 jvm 中的类。我找到的解决方案是:

  • 1st 将代理附加到 pid 指定的 jvm。 (例如 8191)(代码:AttachTest)
  • 2nd 从jvm中已经加载的类(例如8191)中找到要修改的类。
  • 第三次使用仪器添加变压器(代码:AgentMain)
  • 4th 修改transform方法中的类(例如Person)(代码:DemoTransformer)
  • 5th 使用 retransformClasses
  • 重新转换类

    从第 1 步到第 5 步都可以正常工作,但是 retransformClasses 处存在问题。它再次调用了 transform,其中包含修改类的代码。它修改了我不想修改的其他类。
    我认为问题可能发生在 addTransformerretransformClasses 期间。但我还是一头雾水。那么,如何重新转换一个类呢?有任何想法吗?谢谢
    public class AttachTest {
    public static void main(String[] args) throws AttachNotSupportedException,
    IOException, AgentLoadException, AgentInitializationException {
    String agentPath = "D:\\work\\workspace\\myjar\\loaded.jar";
    String vid = args[0];
    VirtualMachine vm = VirtualMachine.attach(vid);
    vm.loadAgent(agentPath);
    }
    }

    //代理人
    public class AgentMain {
    public static void agentmain (String agentArgs, Instrumentation inst)
    throws ClassNotFoundException, UnmodifiableClassException,
    InterruptedException {
    Class<?> [] allLoadedClasses = inst.getAllLoadedClasses();
    String tmpString = null;
    for (int i = 0; i<allLoadedClasses.length; i++) {
    tmpString = allLoadedClasses[i].getName();


    if (0 != tmpString.length()) {
    if (-1 != tmpString.lastIndexOf(".")) {
    tmpString = tmpString.substring(tmpString.lastIndexOf(".")+1,tmpString.length());
    }
    if (tmpString.equals("Person")) {

    inst.addTransformer(new DemoTransformer(), true);
    inst.retransformClasses(allLoadedClasses[i]);

    }
    }
    }
    }
    }

    |
    public class DemoTransformer implements ClassFileTransformer {

    @Override
    public byte[] transform (ClassLoader loader, String className,
    Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
    byte[] classfileBuffer) throws IllegalClassFormatException {

    ModifyMethodTest tm = new ModifyMethodTest(classfileBuffer);

    byte[] byteArray = null;
    try {
    byteArray = tm.modiySleepMethod();

    } catch (Exception e) {

    e.printStackTrace();
    }


    return byteArray;
    }
    }

    输出:
    附加程序
    javax.management.RuntimeMBeanException: java.lang.RuntimeException: Failed to transform [Person]
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.rethrow(DefaultMBeanServerInterceptor.java:856)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.rethrowMaybeMBeanException(DefaultMBeanServerInterceptor.java:869)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:838)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:761)
    at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1427)
    at javax.management.remote.rmi.RMIConnectionImpl.access$200(RMIConnectionImpl.java:72)
    at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1265)
    at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1360)
    at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:788)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:305)
    at sun.rmi.transport.Transport$1.run(Transport.java:159)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:619)
    at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:255)
    at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:233)
    at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:142)
    at com.sun.jmx.remote.internal.PRef.invoke(Unknown Source)
    at javax.management.remote.rmi.RMIConnectionImpl_Stub.invoke(Unknown Source)
    at javax.management.remote.rmi.RMIConnector$RemoteMBeanServerConnection.invoke(RMIConnector.java:993)
    at AttachStackOverflow.main(AttachStackOverflow.java:57)
    Caused by: java.lang.RuntimeException: Failed to transform [Person]
    at loaded3.TransformerService.transform(TransformerService.java:75)
    at loaded3.TransformerService.transformClass(TransformerService.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.sun.jmx.mbeanserver.StandardMBeanIntrospector.invokeM2(StandardMBeanIntrospector.java:93)
    at com.sun.jmx.mbeanserver.StandardMBeanIntrospector.invokeM2(StandardMBeanIntrospector.java:27)
    at com.sun.jmx.mbeanserver.MBeanIntrospector.invokeM(MBeanIntrospector.java:208)
    at com.sun.jmx.mbeanserver.PerInterface.invoke(PerInterface.java:120)
    at com.sun.jmx.mbeanserver.MBeanSupport.invoke(MBeanSupport.java:262)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:836)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:761)
    at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1427)
    at javax.management.remote.rmi.RMIConnectionImpl.access$200(RMIConnectionImpl.java:72)
    at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1265)
    at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1360)
    at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:788)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:305)
    at sun.rmi.transport.Transport$1.run(Transport.java:159)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:619)
    Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)
    at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
    at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:124)
    at loaded3.TransformerService.transform(TransformerService.java:72)
    ... 31 more

    输出: 目标程序
    print Call sayHello()
    print Hello World!
    Supported Redefine
    Supported Retransform
    Call transform() in TransformerService
    Add transformer
    support redefine. return TRUE
    support retransforme. return TRUE
    IsModifiable class "class Person". return TRUE
    Retransform classes
    Number of times to Call transform() in DemoTransformer:1
    ####ASM CODE####
    consturct ModifyMethodTest
    Call modifySleepMethod
    new classreader
    new classwriter
    construct ModifyClassAdapter
    sayHello
    consturct Modifymethod
    [arg1] = java/io/PrintStream [arg2] = println #5
    [arg1] = java/io/PrintStream [arg2] = println #13
    [arg1] = java/util/concurrent/TimeUnit [arg2] = sleep #22
    [arg1] = java/io/PrintStream [arg2] = println #30
    sayHello2
    consturct Modifymethod
    [arg1] = java/io/PrintStream [arg2] = println #5
    [arg1] = java/io/PrintStream [arg2] = println #13
    <init>
    consturct Modifymethod
    [arg1] = java/lang/Object [arg2] = <init> #1
    main
    consturct Modifymethod
    [arg1] = Person [arg2] = <init> #4
    [arg1] = Person [arg2] = sayHello #9
    [arg1] = Person [arg2] = sayHello2 #13
    [arg1] = java/lang/InterruptedException [arg2] = printStackTrace #21
    getName
    consturct Modifymethod
    setName
    consturct Modifymethod
    Call visitend
    Finished to call modifymethodtest
    ####End of ASM CODE
    Remove transformer
    Call transform() in TransformerService
    Add transformer
    support redefine. return TRUE
    support retransforme. return TRUE
    IsModifiable class "class Person". return TRUE
    Retransform classes
    Number of times to Call transform() in DemoTransformer:2
    ####ASM CODE####
    consturct ModifyMethodTest
    Call modifySleepMethod
    new classreader
    new classwriter
    construct ModifyClassAdapter
    sayHello
    consturct Modifymethod
    [arg1] = java/io/PrintStream [arg2] = println #5
    [arg1] = java/io/PrintStream [arg2] = println #13
    [arg1] = java/util/concurrent/TimeUnit [arg2] = sleep #22
    [arg1] = java/io/PrintStream [arg2] = println #30
    sayHello2
    consturct Modifymethod
    [arg1] = java/io/PrintStream [arg2] = println #5
    [arg1] = java/io/PrintStream [arg2] = println #13
    <init>
    consturct Modifymethod
    [arg1] = java/lang/Object [arg2] = <init> #1
    main
    consturct Modifymethod
    [arg1] = Person [arg2] = <init> #4
    [arg1] = Person [arg2] = sayHello #9
    [arg1] = Person [arg2] = sayHello2 #13
    [arg1] = java/lang/InterruptedException [arg2] = printStackTrace #21
    getName
    consturct Modifymethod
    setName
    consturct Modifymethod
    Call visitend
    Finished to call modifymethodtest
    ####End of ASM CODE
    Remove transformer
    print in sayHello()
    print Call sayHello2()
    print Hello World!2

    最佳答案

    简答

  • 不要遍历所有从 Instrumentation 加载的类。相反,只需检查传递给转换器的类名,如果它与您的目标类匹配,则对其进行转换。否则,只需返回未修改的传递的 classfileBuffer。
  • 在转换器外部进行设置调用(即在您的情况下,从您的代理执行以下操作)因此使用您要转换的类名初始化转换器(这将是内部格式,而不是 foo. bar.Snafu ,您将寻找与 foo/bar/Snafu 匹配。然后添加转换器,调用 retransform 然后删除转换器。
  • 为了调用retransform,你需要实际的[pre-transform]类,你可以通过调用Class.forName(在agentmain中)找到它,或者如果你绝对需要,你可以在Intrumentation.getAllLoadedClasses()中找到它作为最后的手段。如果目标类尚未加载,您将需要类加载器调用 Class.forName(name, boolean, classloader),在这种情况下,您可以将 URL 传递到代理主字符串 args 中的目标类类路径。

  • 长答案

    这里有一些建议:
  • 将您正在调用的操作分成 2 个单独的操作:
  • 安装代理。这只需要做一次。
  • 转换目标类[es]。您可能想要这样做 n 次。
  • 我将通过在安装代理时注册一个简单的 JMX MBean 来实现 1.2。此 MBean 应提供类似 public void transformClass(String className) 的操作。并且应该使用对代理获取的 Instrumentation 实例的引用进行初始化。 MBean 类、接口(interface)和任何所需的第 3 方类应包含在您的代理的 loaded.jar 中。它还应该包含您的 ModifyMethodTest 类(我认为它已经这样做了)。
  • 在安装agent jar的同时,还要从 $JAVA_HOME/lib/management-agent.jar 安装管理代理,这将激活管理代理,这样你就可以调用MorreBean的转换操作注册。
  • 参数化您的 DemoTransformer 类以接受您要转换的类名称的内部形式。 (即,如果您的二进制类名是 foo.bar.Snafu,则内部表单将是 foo/bar/Snafu。当您的 DemoTransformer 实例开始获取转换回调时,请忽略与您指定的内部表单类名不匹配的所有类名。 (即简单地返回未修改的 classfileBuffer)
  • 你的变压器 MBean transformClass 操作应该:
  • 将传入的类名转换为内部形式。
  • 创建一个新的DemoTransformer,传递内部表单类名。
  • 使用 Instrumentation.addTransformer(theNewDemoTransformer, true) 注册 DemoTransformer 实例。
  • 调用 Instrumentation.retransformClasses(ClassForName(className))(将二进制类名传递给 MBean 操作)。当此调用返回时,您的类将被转换。
  • Intrumentation.removeTransformer(theNewDemoTransformer) 移除变压器。

  • 以下是我的意思的未经测试的近似值:

    转换器 MBean
    public interface TransformerServiceMBean {
    /**
    * Transforms the target class name
    * @param className The binary name of the target class
    */
    public void transformClass(String className);
    }

    变压器服务
    public class TransformerService implements TransformerServiceMBean {
    /** The JVM's instrumentation instance */
    protected final Instrumentation instrumentation;

    /**
    * Creates a new TransformerService
    * @param instrumentation The JVM's instrumentation instance
    */
    public TransformerService(Instrumentation instrumentation) {
    this.instrumentation = instrumentation;
    }

    /**
    * {@inheritDoc}
    * @see com.heliosapm.shorthandexamples.TransformerServiceMBean#transformClass(java.lang.String)
    */
    @Override
    public void transformClass(String className) {
    Class<?> targetClazz = null;
    ClassLoader targetClassLoader = null;
    // first see if we can locate the class through normal means
    try {
    targetClazz = Class.forName(className);
    targetClassLoader = targetClazz.getClassLoader();
    transform(targetClazz, targetClassLoader);
    return;
    } catch (Exception ex) { /* Nope */ }
    // now try the hard/slow way
    for(Class<?> clazz: instrumentation.getAllLoadedClasses()) {
    if(clazz.getName().equals(className)) {
    targetClazz = clazz;
    targetClassLoader = targetClazz.getClassLoader();
    transform(targetClazz, targetClassLoader);
    return;
    }
    }
    throw new RuntimeException("Failed to locate class [" + className + "]");
    }

    /**
    * Registers a transformer and executes the transform
    * @param clazz The class to transform
    * @param classLoader The classloader the class was loaded from
    */
    protected void transform(Class<?> clazz, ClassLoader classLoader) {
    DemoTransformer dt = new DemoTransformer(clazz.getName(), classLoader);
    instrumentation.addTransformer(dt, true);
    try {
    instrumentation.retransformClasses(clazz);
    } catch (Exception ex) {
    throw new RuntimeException("Failed to transform [" + clazz.getName() + "]", ex);
    } finally {
    instrumentation.removeTransformer(dt);
    }
    }
    }

    类转换器
    public class DemoTransformer implements ClassFileTransformer {
    /** The internal form class name of the class to transform */
    protected String className;
    /** The class loader of the class */
    protected ClassLoader classLoader;
    /**
    * Creates a new DemoTransformer
    * @param className The binary class name of the class to transform
    * @param classLoader The class loader of the class
    */
    public DemoTransformer(String className, ClassLoader classLoader) {
    this.className = className.replace('.', '/');
    this.classLoader = classLoader;
    }

    /**
    * {@inheritDoc}
    * @see java.lang.instrument.ClassFileTransformer#transform(java.lang.ClassLoader, java.lang.String, java.lang.Class, java.security.ProtectionDomain, byte[])
    */
    @Override
    public byte[] transform(ClassLoader loader, String className,
    Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
    byte[] classfileBuffer) throws IllegalClassFormatException {
    if(className.equals(this.className) && loader.equals(classLoader)) {
    return new ModifyMethodTest(classfileBuffer).modiySleepMethod();
    }
    return classfileBuffer;
    }

    }

    代理
    public class AgentMain {

    public static void agentmain (String agentArgs, Instrumentation inst) throws Exception {
    TransformerService ts = new TransformerService(inst);
    ObjectName on = new ObjectName("transformer:service=DemoTransformer");
    // Could be a different MBeanServer. If so, pass a JMX Default Domain Name in agentArgs
    MBeanServer server = ManagementFactory.getPlatformMBeanServer();
    server.registerMBean(ts, on);
    // Set this property so the installer knows we're already here
    System.setProperty("demo.agent.installed", "true");
    }

    }

    代理安装程序
    public class AgentInstaller {
    /**
    * Installs the loader agent on the target JVM identified in <code>args[0]</code>
    * and then transforms all the classes identified in <code>args[1..n]</code>.
    * @param args The target JVM pid in [0] followed by the classnames to transform
    */
    public static void main(String[] args) {
    String agentPath = "D:\\work\\workspace\\myjar\\loaded.jar";
    String vid = args[0];
    VirtualMachine vm = VirtualMachine.attach(vid);
    // Check to see if transformer agent is installed
    if(!vm.getSystemProperties().contains("demo.agent.installed")) {
    vm.loadAgent(agentPath);
    // that property will be set now,
    // and the transformer MBean will be installed
    }
    // Check to see if connector is installed
    String connectorAddress = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress", null);
    if(connectorAddress==null) {
    // It's not, so install the management agent
    String javaHome = vm.getSystemProperties().getProperty("java.home");
    File managementAgentJarFile = new File(javaHome + File.separator + "lib" + File.separator + "management-agent.jar");
    vm.loadAgent(managementAgentJarFile.getAbsolutePath());
    connectorAddress = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress", null);
    // Now it's installed
    }
    // Now connect and transform the classnames provided in the remaining args.
    JMXConnector connector = null;
    try {
    // This is the ObjectName of the MBean registered when loaded.jar was installed.
    ObjectName on = new ObjectName("transformer:service=DemoTransformer");
    // Here we're connecting to the target JVM through the management agent
    connector = JMXConnectorFactory.connect(new JMXServiceURL(connectorAddress));
    MBeanServerConnection server = connector.getMBeanServerConnection();
    for(int i = 1; i < args.length; i++) {
    String className = args[i];
    // Call transformClass on the transformer MBean
    server.invoke(on, "transformClass", new Object[]{className}, new String[]{String.class.getName()});
    }
    } catch (Exception ex) {
    ex.printStackTrace(System.err);
    } finally {
    if(connector!=null) try { connector.close(); } catch (Exception e) {}
    }
    // Done. (Hopefully)
    }
    }

    ================== 更新 ==================

    嘿尼克;是的,这是当前(即 Java 5-8)类转换器的局限性之一。
    引用 Instrumentation javadoc :

    "The retransformation may change method bodies, the constant pool and attributes. The retransformation must not add, remove or rename fields or methods, change the signatures of methods, or change inheritance. These restrictions maybe be lifted in future versions. The class file bytes are not checked, verified and installed until after the transformations have been applied, if the resultant bytes are in error this method will throw an exception."



    顺便说一句,同样的限制也被逐字记录用于重新定义类。

    因此,您有 2 个选择:
  • 不要添加新方法。这通常是非常有限的,并且取消了使用非常常见的字节码 AOP 模式(如方法)的资格
    包装。根据您使用的字节码操作库,您可能能够注入(inject)您想要的所有功能
    现有的方法。有些库比其他库更容易做到这一点。或者,我应该说,有些库会使这比其他库更容易。
  • 在类加载之前转换类。这使用了我们已经讨论过的代码的相同一般模式,除了您不触发
    通过调用 retransformClasses 进行转换。相反,您注册 ClassFileTransformer 以在加载类之前执行转换
    并且您的目标类将在加载第一类时进行修改。在这种情况下,您可以随意修改类
    就像,只要最终产品仍然可以验证。击败应用程序(即让您的 ClassFileTransformer
    在应用程序加载类之前注册)很可能需要像 javaagent 这样的命令,但如果你有严格的控制
    在应用程序的生命周期中,可以在更传统的应用程序层代码中执行此操作。正如我所说,你只需要制作
    确保在加载目标类之前注册转换器。

  • 您可以使用的 #2 的另一种变体是通过使用新的类加载器来模拟全新的类。如果你创建一个新的
    隔离的类加载器不会委托(delegate)给现有的 [loaded] 类,但可以访问 [unloaded] 目标类字节码,
    由于 JVM 认为这是一个全新的类,因此您实际上是在重现上面 #2 的要求。

    ================ 更新 ================

    在你最后的评论中,我觉得我有点忘记你在哪里了。无论如何,Oracle JDK 1.6 绝对支持重新转换。我对 ASM 不太熟悉,但是您发布的最后一个错误表明 ASM 转换以某种方式修改了不允许的类架构,因此重新转换失败。

    我认为一个有效的例子会增加更多的清晰度。与上述相同的类(加上一个名为 Person 的测试类)是 here 。有一些修改/添加:
  • TransformerService 中的变换操作现在有 3 个参数:
  • 二进制类名
  • 仪器的方法名称
  • 一个 [regular] 表达式来匹配方法签名。 (如果为 null 或为空,则匹配所有签名)
  • 实际的字节码修改是使用 Javassist 类中的 ModifyMethodTest 完成的。检测所做的只是添加一个 System.out.println,如下所示:-->Invoked method [com.heliosapm.shorthandexamples.Person.sayHello((I)V)]
  • AgentInstaller(有演示的Main)只是自己安装代理和转换服务。 (更容易用于开发/演示目的,但仍可与其他 JVM 一起使用)
  • 代理自安装后,主线程创建一个 Person 实例并循环调用 Person 的两个 sayHello 方法。

  • 在转换之前,该输出如下所示。
    Temp File:c:\temp\com.heliosapm.shorthandexamples.AgentMain8724970986698386534.jar
    Installing AgentMain...
    AgentMain Installed
    Agent Loaded
    Instrumentation Deployed:true
    Hello [0]
    Hello [0]
    Hello [1]
    Hello [-1]
    Hello [2]
    Hello [-2]

    Person 有 2 个 sayHello 方法,一个采用 int ,另一个采用 String 。 (字符串一只是打印循环索引的负数)。

    一旦我启动了 AgentInstaller,代理就被安装并且 Person 被循环调用,我使用 JConsole 连接到 JVM:

    Finding the AgentInstaller JVM

    我导航到 TransformerService MBean 并调用 transformClass 操作。我提供了完全限定的类 [二进制] 名称、要检测的方法名称和一个正则表达式 (I)V ,它仅匹配以 int 作为参数的 sayHello 方法。 (或者我可以提供 .* ,或者不匹配所有重载)。我执行操作。

    Invoking the operation

    现在,当我回到正在运行的 JVM 并检查输出时:
    Examining class [com/heliosapm/shorthandexamples/Person]
    Instrumenting class [com/heliosapm/shorthandexamples/Person]
    [ModifyMethodTest] Adding [System.out.println("\n\t-->Invoked method [com.heliosapm.shorthandexamples.Person.sayHello((I)V)]");]
    [ModifyMethodTest] Intrumented [1] methods

    -->Invoked method [com.heliosapm.shorthandexamples.Person.sayHello((I)V)]
    Hello [108]
    Hello [-108]

    -->Invoked method [com.heliosapm.shorthandexamples.Person.sayHello((I)V)]
    Hello [109]
    Hello [-109]

    完毕。方法检测。

    请记住,允许重新转换的原因是 Javassist 字节码修改除了将代码注入(inject)现有方法之外没有进行任何更改。

    合理 ?

    关于java - 如何在运行时重新转换类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18567552/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com