如何在运行时重新转换类?

2023-12-24

我正在尝试修改一个已加载到 JVM 中的类。我找到的解决方案是这样的:

  1. 将代理附加到 PID 指定的 JVM。 (例如8191)(代码:AttachTest)
  2. 从 JVM 中已加载的类中找到您要修改的类(例如 8191)。
  3. 使用仪器添加变压器(代码:AgentMain)
  4. 修改类(例如Person) in transform方法(代码:DemoTransformer)
  5. 使用重新转换类retransformClasses https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html#RetransformClasses

从第1步到第5步工作正常,但在以下位置出现问题retransformClasses。它叫transform再次包含修改类的代码。它修改了我不想修改的其他类。 我认为问题可能发生在addTransformer or retransformClasses。但我还是很困惑。那么,如何重新改造一个类呢?

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

//Agent

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

OUTPUTS: 附加程序

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

OUTPUTS: 目标计划

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. 搞砸了,您将寻找匹配的富/酒吧/混乱。然后添加变压器,调用 retransform,然后删除变压器。
  • 为了调用 retransform,您将需要实际的 [pre-transform] 类,您可以通过调用找到该类名称类(在agentmain中),或者如果你绝对有必要,你可以在Instrumentation.get 加载的类()作为最后的手段。如果目标类尚未加载,则需要调用类加载器Class.forName(名称,布尔值,类加载器)在这种情况下,您可以将 URL 传递到代理主字符串参数中的目标类类路径。

长答案

这里有一些建议:

  1. Separate out the operation you're calling into 2 separate operations:
    1. 安装代理。这只需要完成一次。
    2. 转换目标类。你可能想要这样做n times.
  2. 我将通过在安装代理时注册一个简单的 JMX MBean 来实现 1.2。这个 MBean 应该提供类似的操作public void transformClass(String className)。并且应该参考代理获取的信息进行初始化仪器仪表实例。 MBean 类、接口和任何必需的第三方类应包含在您的代理的加载的.jar。它还应该包含您的修改方法测试类(我认为它已经这样做了)。
  3. 在安装代理 jar 的同时,还要安装管理代理$JAVA_HOME/lib/management-agent.jar这将激活管理代理,以便您可以在要注册的 MBean 中调用转换操作。
  4. 参数化您的 DemoTransformer 类以接受内部形式您要转换的类的名称。 (即,如果您的二进制类名称是foo.bar. 搞砸了,内部形式将是富/酒吧/混乱。当您的 DemoTransformer 实例开始获取转换回调时,请忽略与您指定的内部表单类名不匹配的所有类名。 (即简单地返回未修改的classfileBuffer)
  5. Your tranformer MBean transformClass operation should then:
    1. 将传递的类名转换为内部形式。
    2. 创建一个新的 DemoTransformer,传递内部表单类名。
    3. 使用注册 DemoTransformer 实例Instrumentation.addTransformer(theNewDemoTransformer, true) .
    4. Call Instrumentation.retransformClasses(ClassForName(className))(将二进制类名传递给 MBean 操作)。当此调用返回时,您的类将被转换。
    5. 拆下变压器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)类转换器的限制之一。 引用自仪器javadoc http://docs.oracle.com/javase/7/docs/api/java/lang/instrument/Instrumentation.html:

“重新转换可能会改变方法体、常量池和 属性。重新转换不得添加、删除或重命名字段 或方法,更改方法的签名,或更改继承。 这些限制可能会在未来版本中取消。类文件 直到之后才检查、验证和安装字节 如果结果字节有错误,则已应用转换 这个方法会抛出异常。”

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

因此,您有 2 个选择:

  1. 不要添加新方法。这通常是非常有限的,并且取消了非常常见的字节码 AOP 模式(如方法)的使用资格 包装。根据您使用的字节码操作库,您也许能够注入您想要的所有功能 现有的方法。有些库比其他库更容易做到这一点。或者,我应该说,有些库会让这比其他库更容易。

  2. 在类加载之前转换类。这使用了我们已经讨论过的代码的相同通用模式,只是您不触发 通过调用 retransformClasses 进行转换。相反,您注册 ClassFileTransformer 来执行转换before类已加载 并且您的目标类将在加载第一个类时被修改。在这种情况下,您几乎可以自由地以任何方式修改该类 就像,只要最终产品仍然可以被验证。击败应用程序(即让你的 ClassFileTransformer 在应用程序加载类之前注册)很可能需要像这样的命令java代理,尽管如果你有严格的控制 在应用程序的生命周期中,可以在更传统的应用程序层代码中执行此操作。正如我所说,你只需要做 确保在加载目标类之前注册变压器。

您可以使用的#2 的另一种变体是simulate使用新的类加载器创建一个全新的类。如果您创建一个新的 隔离的类加载器不会委托给现有的[已加载]类,但可以访问[已卸载]目标类字节码, 你本质上是在重现上面#2 的要求,因为 JVM 认为这是一个全新的类。

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

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

我认为一个工作示例会更加清晰。与上面相同的类(加上一个名为 Person 的测试类)是here https://gist.github.com/nickman/6494990。有一些修改/添加:

  • The transform operation in the TransformerService https://gist.github.com/nickman/6494990#file-transformerservice-java now has 3 parameters:
    1. 二进制类名
    2. 仪器的方法名称
    3. 与方法签名匹配的[正则]表达式。 (如果为 null 或为空,则匹配所有签名)
    4. 实际的字节码修改是使用完成的Java助手 http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/ in the 修改方法测试 https://gist.github.com/nickman/6494990#file-modifymethodtest-java班级。所有仪器所做的就是添加一个系统输出打印文件看起来像这样:-->Invoked method [com.heliosapm.shorthandexamples.Person.sayHello((I)V)]
  • The 代理安装程序 https://gist.github.com/nickman/6494990#file-agentinstaller-java(其中有演示的 Main)只需自行安装代理和转换服务。 (更容易用于开发/演示目的,但仍可与其他 JVM 一起使用)
  • 一旦代理自行安装,主线程就会创建一个Person https://gist.github.com/nickman/6494990#file-person-java实例并只是循环,调用 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]

人有2sayHello方法,需要一种int,另一个需要一个String。 (字符串一仅打印循环索引的负数)。

一旦启动 AgentInstaller,代理就会安装完毕,Person 就会在循环中被调用,我会使用 JConsole 连接到 JVM:

我导航到 TransformerService MBean 并调用变换类手术。我提供完全限定的类 [二进制] 名称、仪器的方法名称以及正则表达式(I)V哪个匹配only the sayHello以 int 作为参数的方法。 (或者我可以提供.*,或者没有任何东西可以匹配所有重载)。我执行操作。

现在,当我返回正在运行的 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 字节码修改除了将代码注入现有方法之外没有进行任何更改。

合理 ?

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在运行时重新转换类? 的相关文章

  • 在 MongoDB Java 驱动程序中如何使用 $filter

    我有一个适用于 MQL 的查询 我需要将其翻译成Java MQL 中的查询如下所示 db
  • 使用 Spring MVC 返回 PDF 文件

    实际上 我有这个功能 我有一个框架 可以在其中设置 URL ip port birt preview report report rptdesign format pdf parameters 并且该框架呈现 PDF 文件 但我想隐藏该网址
  • 我们如何测试我们的 Java UI?

    我们正在寻找记录和回放类型的测试工具来自动化我们的一些 UI 功能测试 我们已经研究了从 Silke 到 QTP 的大多数常见嫌疑 但没有一个起作用 当需要右键单击才能从右键单击菜单中选择某些内容时 或者当您必须在网格的下拉列表中选择一个值
  • 使用 java 的 RAR 档案 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Java 9 中可以使用提前编译吗?

    As per JEP 295 http openjdk java net jeps 295 任何 JDK 模块 类或用户代码的 AOT 编译都是实验性的 JDK 9 中不支持 要使用 AOT 化的 java base 模块 用户必须编译该模
  • 我需要显式关闭连接吗?

    我持有一个实例MongoClient and DB在我的应用程序中 每次我想执行某些操作时 我都会调用getCollection 我想知道是否需要显式关闭连接 就像connection close 在 JDBC 中 强调一下 我只有一个Mo
  • 确定代码是否在 App Engine 运行时 (Java) 上运行

    如何确定某些代码 Serv let 或简单的类 是否正在 Google App Engine 运行时 Java 上运行 以便决定是否使用 App Engine 的特定库 是否有一些可靠的运行时环境 ID 您可以检查com google ap
  • MongoDB:尝试从 JSON 读取 Long 导致 java.lang.Integer 无法转换为 java.lang.Long

    我有一个代码可以从 MongoDB 读取特定格式的数据 我需要测试一下 为此 我使用要测试的数据创建一个 JSON id ObjectId 57552e32e4b0839ede67e0af serial 574000690 startDat
  • Spring 术语中命令、表单、业务和实体对象之间的区别?

    我试图理解这些对象在松散耦合系统方面的差异 业务对象与实体对象相同吗 我可以使用 MVC 中的业务或实体对象作为我的命令对象吗 命令对象与表单对象相同吗 只是寻找 Spring 术语和用法中对象类型的说明 我在 stackoverflow
  • 将 EditText 聚焦在设备上运行的 PopupWindow 中时出现异常

    我正在为 Android 开发一个弹出窗口 它正在工作 我在上面添加了一个 EditText 和一个按钮 当在 ADV 上运行时 它可以正常工作 而在设备上运行时 当我专注于 EditText 时 这会抛出一个奇怪的异常 android v
  • 在 JavaFX 中拖动未装饰的舞台

    我希望将舞台设置为 未装饰 使其可拖动且可最小化 问题是我找不到这样做的方法 因为我遇到的示例是通过插入到主方法中的方法来实现的 我想通过控制器类中声明的方法来完成此操作 就像我如何使用下面的 WindowClose 方法来完成此操作 这是
  • 驱动程序信息:driver.version:未知,使用 ChromeDriver v78.0.3904.70 和 Chrome 浏览器 v78.0.3904.97

    我使用的是java 1 8和chrome浏览器版本78 0 3904 97 我正在尝试使用 chrome 驱动程序版本执行我的 selenium 脚本代码78 0 3904 70 但在执行时我面临以下问题并且 chrome 立即崩溃 Pic
  • java Runtime.getRunTime().exec 和通配符?

    我正在尝试使用删除垃圾文件 Process p Runtime getRuntime exec 只要我不使用通配符 它 就可以正常工作 即 Process p Runtime getRuntime exec bin rm f specifi
  • 处理照片上传的最佳方式是什么?

    我正在为一个家庭成员的婚礼制作一个网站 他们要求的一个功能是一个照片部分 所有客人都可以在婚礼结束后前往并上传他们的照片 我说这是一个很棒的想法 然后我就去实现它 那么只有一个问题 物流 上传速度很慢 现代相机拍摄的照片很大 2 5 兆 我
  • 我可以关闭并重新打开套接字吗?

    我学习了一个使用套接字的例子 在此示例中 客户端向服务器发送请求以打开套接字 然后服务器 侦听特定端口 打开套接字 一切都很好 套接字从双方 客户端和服务器 打开 但我仍然不清楚这个东西有多灵活 例如 客户端是否可以关闭一个打开的 从两端
  • 将变量从 jenkins 传递到 testng.xml

    我想根据从詹金斯传递的变量运行测试用例 例如 选择您要运行的测试用例 测试用例一 测试用例二 在 pom xml maven 中
  • Apache HttpClient TCP Keep-Alive(套接字保持活动)

    我的 http 请求需要太多时间才能被服务器处理 大约 5 分钟 由于连接闲置 5 分钟 代理服务器将关闭连接 我正在尝试在 Apache DefaultHttpClient 中使用 TCP Keep Alive 来使连接长时间处于活动状态
  • 如何配置嵌入式 MongoDB 以在 Spring Boot 应用程序中进行集成测试?

    我有一个相当简单的 Spring Boot 应用程序 它公开一个小型 REST API 并从 MongoDB 实例检索数据 对 MongoDB 实例的查询通过基于 Spring Data 的存储库 下面的一些关键代码 Main applic
  • 使用 Hibernate 防止无限循环数据检索

    我想知道 想象一个场景 例如 POJO public class User private String userName private String name private String surname private List
  • 安卓框架?

    是否有任何框架比构建 Android 应用程序更容易 您会对其中一个感兴趣吗 很快就会有 我正在开发 DroidFu 一个 Android 共享库 它将为您提供 活动 和服务 中直接提供大量实用功能 例如生成列表和错误对话框 检查 Inte

随机推荐

  • UILabel 动画不正确

    由于某种原因 UILabel 的文本想要在没有动画的情况下设置其对齐方式 并且我不知道如何使文本与标签的其余部分一起动画 我现在有以下代码 UILabel label UILabel alloc initWithFrame CGRectMa
  • 如何在不重新加载和 # hack 的情况下更改 Windows 位置?

    起初我认为哈希破解是一个要求 但是从 Facebook 最近的更新来看 我不这么认为 最初的 hash hack 我不确定这是否是正确的术语 是通过更改 location hash 可以在 URL 中保存状态而不刷新页面 这被谷歌的应用程序
  • 在 Swift 中转义反斜杠

    我将正则表达式作为字符串值发送到 CloudKit 但它似乎不喜欢它 将 by 然而 一旦我从我的应用程序中获得这个值 我想以其原始形式重新转换它 代替 我不知道如何在 Swift 中管理这种转义字符 因为我什至无法用 a 设置字符串 在我
  • 如何加速“独特”数据框搜索

    我有一个数据框 其尺寸为 2377426 行 x 2 列 如下所示 Name Seq 428293 ENSE00001892940 ENSE00001929862 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  • 如何每隔x秒重复执行一个函数?

    我想永远每 60 秒重复执行一次 Python 中的函数 就像NSTimer http web archive org web 20090823012700 http developer apple com 80 DOCUMENTATION
  • java持久化内存泄漏

    我的表中有 100 万行 我想获取所有行 但是当我尝试通过分页获取带有 jpa 的所有行时 我收到 java 堆错误 你认为我错过了什么吗 任何建议 int counter 0 while counter gt 0 javax persis
  • 在 Docker Alpine 上安装seaborn

    我正在尝试安装seaborn使用这个 Dockerfile FROM alpine latest RUN apk add update python py pip python dev RUN pip install seaborn CMD
  • 获取客户端隐藏字段的值

    单击服务器端的按钮 我将表中的列中的值分配给隐藏字段 Dim dsGetEnquiryDetails dbl usp GetEnquiryRegisterDetails Val lblEnquiryRegisterID Text AsQue
  • Docker:Opensearch 拒绝与 docker 中的 opensearch 文档中的示例连接

    我正在 docker 容器上运行 opensearch v 1 0 0 并在localhost 请考虑这个问题IS NOT和这篇文章一样 Opensearch Docker Image 无法建立新连接 Errno 111 连接被拒绝 htt
  • 对贝塞尔曲线的点进行动画处理[重复]

    这个问题在这里已经有答案了 是否可以对贝塞尔曲线的点进行动画处理 我正在尝试从直线到箭头的平滑过渡 这是该行在代码中的样子 Color Declarations UIColor white UIColor colorWithRed 1 gr
  • 计算沿轴的直方图

    有没有办法沿着 nD 数组的轴计算许多直方图 我目前使用的方法是for循环迭代所有其他轴并计算numpy histogram 对于每个生成的一维数组 import numpy import itertools data numpy rand
  • C++11/14 中的 Boost.Pointer 容器被 std::unique_ptr 废弃了?

    Does std unique ptr make Boost Pointer容器C 11 14 中的库已过时吗 在 C 98 03 中没有移动语义 并且有一个智能指针 例如shared ptr与引用计数相关overhead 对于参考计数块
  • 使 FAB 响应软键盘显示/隐藏更改

    我看过各种关于 FAB 响应屏幕底部 Snackbar 弹出窗口以及滚动敏感 FAB 的帖子 但是否有一些实施FloatingActionButton Behavior 或类似 将 FAB 移至键盘上方当它出现时 现在 当我单击某个按钮时
  • 将 IE 窗口置于屏幕前面

    我正在动态创建新的 IE 浏览器实例 并从那里打开一个 aspx 页面 一切正常 但浏览器没有在屏幕前面弹出 当我从那里单击它时 能够在任务栏中看到 Aspx 页面 它会出现在前面 如何在 IE 创建后立即将该页面显示在所有屏幕的前面 我已
  • 如何处理来自不同时区的日期时间

    我有一个 django 应用程序 它在数据库 postgres 中存储 UTC 的日期时间 它在世界各地都有用户 但在应用程序逻辑中 我根据本地时间范围进行了一些验证 即用户在瓜亚基尔并且整个周日都发生了一些事情 我在执行它时遇到问题并进行
  • 调用线程无法访问该对象,因为另一个线程拥有它

    我正在尝试从 PowerShell 检索打印队列列表 如下所示 但我越来越 The calling thread cannot access this object because a different thread owns it 发生
  • 如何在Python中进行二次排序?

    如果我有一个数字列表 4 2 5 1 3 我想先按某个功能对其进行排序f然后对于具有相同值的数字f我希望它按数字的大小排序 这段代码似乎不起作用 list5 sorted list5 list5 sorted list5 key lambd
  • webpack 在react.js 中无法正常工作

    我使用创建了一个 hello world 反应应用程序create react app命令 然后我尝试使用运行相同的文件webpack 但它不能正常工作 比如 ico css文件是not rendering到屏幕上 请帮我解决这个问题 we
  • 在 Observable Angular js 2 中迭代 json 字符串

    以下是我的html代码 tr td c name td td c skill td tr 在我的 json 中 name abc skill xyz 这是可行的 但我需要迭代这个 json 字符串 var obj a 1 b 2 for v
  • 如何在运行时重新转换类?

    我正在尝试修改一个已加载到 JVM 中的类 我找到的解决方案是这样的 将代理附加到 PID 指定的 JVM 例如8191 代码 AttachTest 从 JVM 中已加载的类中找到您要修改的类 例如 8191 使用仪器添加变压器 代码 Ag