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

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(使用前将#替换为@)

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

  • 是否可以在 Spring Batch 中结合分区和并行步骤?

    我只是想知道它在 Spring Batch 中可行吗 Step1Step2 流程 gt 流程1 流程2 流程3 Step3 其中每个flow1 gt 划分为 5 个 GridSizeflow2 gt 划分为 5 个 GridSizeflow
  • java中的csv到pdf文件

    我正在尝试获得一个csv文件解析为pdf 到目前为止我所拥有的内容附在下面 我的问题是这段代码最终出现在 pdf 中的文件在 csv 文件的第一行被截断 我不明白为什么 附示例 本质上我想要一个没有任何操作的 csv 文件的 pdf 版本
  • 用 @DataJpaTest 注释的测试不是用 @Autowired 注释的自动装配字段

    我有一个 Spring Boot 应用程序 其中包含 Spring Data Jpa 存储库 我需要围绕这个存储库运行单元 或组件 测试 我对 Spring Data Jpa 没有太多经验 这是我的测试 这很简单 我无法让它通过 impor
  • Java 创建浮雕(红/蓝图像)

    我正在编写一个 Java 游戏引擎 http victoryengine org http victoryengine org 并且我一直在尝试生成具有深度的 3D 图像 您可以使用那些红色 蓝色眼镜看到 我正在使用 Java2D 进行图形
  • 使用 Checkstyle Plugin 时从插件调用代码时出现问题:“org.eclipse.jface”

    我正在尝试在 Rational Software Architect 7 0 0 4 上使用 eclipse cs 插件 我最近卸载了旧的 beta2 版本并安装了 beta3 插件本身按照之前的配置工作 但是每当我尝试通过 Windows
  • 无法加载 jar 文件的主类

    我使用 Eclipse IDE 开发了一个应用程序 创建应用程序后 我以 jar 格式导出项目 当我尝试运行此 jar 文件时 出现错误 无法加载主类 请帮忙 当您将项目导出为 jar 时 请参阅此所以问题 https stackoverf
  • 通过 InjectMocks Spy 注入对象

    我需要对一个类运行一系列单元测试 该类具有 Autowired Logger 实现 实现的基本思想是 Mock Logger logger InjectMocks TestedClass tested 但我想保存日志输出功能 Mockito
  • 有没有好的方法来解析用户代理字符串?

    我有一个Java接收模块User Agent来自最终用户浏览器的字符串的行为需要略有不同 具体取决于浏览器类型 浏览器版本甚至操作系统 例如 FireFox 7 0 Win7 Safari 3 2 iOS9 我明白了User Agent由于
  • 如何对 IntStream 进行逆序排序

    我正在使用 txt 文件读取数字BufferedReader 我想颠倒该流中元素的顺序 以便在收集它们时 它们将从最高到最低排列 我不想在构建数组后进行排序 因为我不知道其中可能有多少元素 我只需要最高的 N 个元素 in new Buff
  • PropertySources 中各种源的优先级

    Spring引入了新的注释 PropertySources对于所有标记为的类 Configuration since 4 0 需要不同的 PropertySource作为论证 PropertySources PropertySource c
  • Spring Stomp over Websocket:流式传输大文件

    我的SockJs客户端在网页中 发送帧大小为16K的消息 消息大小限制决定了我可以传输的文件的最大大小 以下是我在文档中找到的内容 Configure the maximum size for an incoming sub protoco
  • Java:VM 如何在 32 位处理器上处理 64 位“long”

    JVM 如何在 32 位处理器上处理 64 位的原始 long 在多核 32 位机器上可以并行利用多个核心吗 64 位操作在 32 位机器上慢了多少 它可能使用多个核心来运行不同的线程 但不会并行使用它们进行 64 位计算 64 位长基本上
  • 尝试在没有 GatewayIntent 的情况下访问消息内容

    我希望每当我写一条打招呼的消息时 机器人都会在控制台中响应一条消息 但它只是给我一个错误 JDA MainWS ReadThread WARN JDA Attempting to access message content without
  • 如何在java中使jpeg无损?

    有没有人可以告诉我如何使用编写 jpeg 文件losslessjava中的压缩 我使用下面的代码读取字节来编辑字节 WritableRaster raster image getRaster DataBufferByte buffer Da
  • 如何使用 Mockito 和 Junit 模拟 ZonedDateTime

    我需要模拟一个ZonedDateTime ofInstant 方法 我知道SO中有很多建议 但对于我的具体问题 到目前为止我还没有找到任何简单的解决办法 这是我的代码 public ZonedDateTime myMethodToTest
  • 从 Stax XMLStreamReader 读取以解组部分

    我正在使用 Stax 游标 API 从大型 xml 文件中提取数据 当前 我转到特殊标签的开头并使用 JAXB 解组该标签 这对于格式良好的 xml 文件效果很好 但不久前我有一个文档 其中数十万个标签中有一个未关闭 JAXB 使用 XML
  • 阻止 OSX 变音符号为所有用户禁用 Java 中的 KeyBindings?

    注 我知道这个问题 https stackoverflow com questions 40335285 java keybinds stop working after holding down a key用户必须输入终端命令才能解决此问
  • Path2D 上的鼠标指针检测

    我构建了一个Path2D http docs oracle com javase 7 docs api java awt geom Path2D html表示由直线组成的未闭合形状 我希望能够检测何时单击鼠标并且鼠标指针靠近路径 在几个像素
  • 为什么 BufferedWriter 不写入文件?

    我有这个代码 String strings Hi You He They Tetrabenzene Caaorine Calorine File file new File G words txt FileWriter fWriter Bu
  • Java 推断泛型类型

    我正在寻找类似的推断捕获泛型类型的概念 类似于以下方法片段 但不是捕获泛型类型的类 public

随机推荐

  • 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