java - रनटाइम पर किसी क्लास को कैसे अट्रैक्टिव करें



jvm aop (1)

मैं संशोधित वर्ग को ट्रिंग कर रहा हूं जो पहले से ही एक jvm में लोड है। जो समाधान मुझे मिला वह है:

  • 1 pid द्वारा निर्दिष्ट jvm के लिए एक एजेंट संलग्न करें। (उदाहरण के लिए 8191) (कोड: अटैचेस्ट)
  • 2 वह वर्ग ढूंढें जो आप उन लोगों से संशोधित करना चाहते हैं जो पहले से ही jvm में लोड किए गए हैं (उदाहरण 8191)
  • साधन (कोड: AgentMain) का उपयोग करके तीसरा ट्रांसफ़ॉर्मर जोड़ें
  • 4 विधि transform में वर्ग (जैसे व्यक्ति) को संशोधित करें (कोड: DemoTransformer)
  • 5 वीं कक्षा को retransformClasses का उपयोग करके retransformClasses

यह 1 स्टेप से 5 वें स्टेप तक ठीक काम करता है, लेकिन retransformClasses समस्याएं हैं। इसे फिर से transform कहा जाता है जिसमें कक्षा को संशोधित करने के लिए कोड होते हैं। और यह अन्य वर्गों को संशोधित करता है जिन्हें मैं कभी संशोधित नहीं करना चाहता। मुझे लगता है कि addTransformer या 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);
    }
}

// एजेंट

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

परिणाम: ATTACH कार्यक्रम

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

संक्षिप्त जवाब

  • इंस्ट्रूमेंटेशन से सभी भरी हुई कक्षाओं के माध्यम से पुनरावृति न करें। इसके बजाय, ट्रांसफार्मर में पास किए गए वर्ग के नाम की जांच करें और यदि यह आपके लक्ष्य वर्ग से मेल खाता है, तो इसे बदल दें। अन्यथा, बस पारित classfileBuffer unmodified लौटें।
  • ट्रांसफार्मर के बाहर सेट अप कॉल करें, (यानी आपके मामले में, अपने एजेंट से निम्नलिखित करें) ताकि आप जिस ट्रांसफार्मर को बदलना चाहते हैं, उस वर्ग नाम के साथ अपने ट्रांसफार्मर को इनिशियलाइज़ करें (यह foo.bar के बजाय आंतरिक स्वरूप होगा । स्नफू , आप फू / बार / स्नैफू के खिलाफ मैच की तलाश में होंगे। इसके बाद ट्रांसफॉर्मर जोड़ें, रिट्रांसफॉर्म को कॉल करें और फिर ट्रांसफॉर्मर को हटा दें।
  • रिट्रांसफॉर्म को कॉल करने के लिए, आपको वास्तविक [पूर्व-परिवर्तन] वर्ग की आवश्यकता होगी जिसे आप Class.forName (Agentmain) में कॉल करके पा सकते हैं, या यदि आपको पूरी तरह से करना है, तो आप इसे Intrumentation.getAlLoadedClasses () के रूप में पा सकते हैं। अ लास्ट रिसॉर्ट। यदि लक्ष्य वर्ग लोड नहीं किया गया है, तो आपको क्लास लोडर को Class.forName (नाम, बूलियन, क्लास लोडर) पर कॉल करने की आवश्यकता होगी , जिस स्थिति में आप एजेंट मुख्य स्ट्रिंग आर्ग्स में लक्ष्य वर्ग-पथ के लिए URL पास कर सकते हैं।

लंबा जवाब

यहाँ कुछ सिफारिशें दी गई हैं:

  1. उस ऑपरेशन को अलग करें जिसे आप 2 अलग-अलग ऑपरेशनों में बुला रहे हैं:
    1. एजेंट स्थापित करें। यह केवल एक बार किया जाना चाहिए।
    2. लक्ष्य वर्ग [तों] को रूपांतरित करें। आप इसे n बार करना चाहते हैं।
  2. जब आप एजेंट को स्थापित करते हैं तो मैं एक साधारण JMX MBean दर्ज करके 1.2 को लागू करूंगा। इस public void transformClass(String className) जैसा एक ऑपरेशन प्रदान करना चाहिए। और एजेंट के अधिग्रहीत इंस्ट्रुमेंटेशन उदाहरण के संदर्भ में आरम्भ किया जाना चाहिए। एमबीएन वर्ग, इंटरफ़ेस और किसी भी आवश्यक 3 पार्टी वर्गों को आपके एजेंट के लोडेड.जर में शामिल किया जाना चाहिए। इसमें आपका संशोधितमिथोडेस्ट वर्ग भी होना चाहिए (जो मुझे लगता है कि यह पहले से ही है)।
  3. उसी समय जब आप अपने एजेंट जार को स्थापित करते हैं, तो $ JAVA_HOME / lib / प्रबंधन-agent.jar से प्रबंधन एजेंट भी स्थापित करें जो प्रबंधन एजेंट को सक्रिय करेगा ताकि आप जिस MB के बारे में पंजीकरण करने जा रहे हैं उसमें आप ट्रांस्फ़ॉर्म ऑपरेशन को लागू कर सकें।
  4. आप जिस वर्ग को बदलना चाहते हैं, उसके नाम के आंतरिक रूप को स्वीकार करने के लिए अपने डेमोट्रांसफॉर्मर वर्ग को परिमित करें। (यानी यदि आपके बाइनरी क्लास का नाम foo.bar.Snafu है , तो आंतरिक रूप foo / bar / Snafu होगा । जैसा कि आपके DemoTransformer उदाहरण से कॉलबैक बदलना शुरू हो जाता है, उन सभी क्लास नामों को अनदेखा करें जो आपके द्वारा निर्दिष्ट आंतरिक फॉर्म नाम से मेल नहीं खाते हैं। (यानी बस classfileBuffer unmodified लौटें)
  5. आपका ट्रांसफॉर्मर एमबी ट्रांसफॉर्मलस ऑपरेशन तब होना चाहिए:
    1. उत्तीर्ण वर्ग के नाम को आंतरिक रूप में परिवर्तित करें।
    2. आंतरिक फॉर्म क्लास नाम से गुजरते हुए एक नया डेमोट्रांसफॉर्मर बनाएं।
    3. Instrumentation.addTransformer(theNewDemoTransformer, true) . Instrumentation.addTransformer(theNewDemoTransformer, true) का उपयोग करके डेमोट्रांसफॉर्मर इंस्टेंस को पंजीकृत करें।
    4. Instrumentation.retransformClasses(ClassForName(className)) (बाइनरी क्लास नाम के साथ Instrumentation.retransformClasses(ClassForName(className)) ऑपरेशन के लिए दिया गया)। जब यह कॉल वापस आएगा, तो आपकी कक्षा बदल जाएगी।
    5. Intrumentation.removeTransformer(theNewDemoTransformer) साथ ट्रांसफार्मर निकालें।

निम्नलिखित का तात्पर्य अप्रयुक्त अनुमान है जो मेरा मतलब है:

ट्रांसफार्मर एमबीएन

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

================ अद्यतन ==================

हे निक; हां, यह वर्तमान (यानी जावा 5-8) वर्ग ट्रांसफार्मर की सीमाओं में से एक है। इंस्ट्रूमेंटेशन javadoc से उद्धृत करने के लिए:

"रिट्रांसेशन मेथड बॉडीज, कंटीन्यूअस पूल और एट्रिब्यूट्स को बदल सकता है। रिट्रांसेशन को फील्ड्स या मेथड्स को जोड़ना, हटाना या रीनेम नहीं करना चाहिए, मेथड्स के सिग्नेचर को बदलना या इनहेरिटेंस को बदलना चाहिए। इन प्रतिबंधों को शायद भविष्य के वर्जन में उठा लिया जाए। क्लास फाइल बाइट्स ट्रांसफ़ॉर्मेशन लागू होने के बाद तक जाँच, सत्यापन और स्थापना नहीं की जाती है, यदि परिणामी बाइट्स त्रुटि में हैं, तो यह विधि एक अपवाद फेंक देगी। "

एक तरफ के रूप में, यह एक ही सीमा वर्गों के पुनर्वितरण के लिए शब्दशः भी प्रलेखित है।

इस प्रकार, आपके पास 2 विकल्प हैं:

  1. नए तरीके न जोड़ें। यह आमतौर पर बहुत सीमित है और विधि रैपिंग जैसे बहुत सामान्य बाइट-कोड एओपी पैटर्न के उपयोग को अयोग्य घोषित करता है। आप किस बाइट-कोड हेरफेर लाइब्रेरी का उपयोग कर रहे हैं, इसके आधार पर, आप मौजूदा विधियों में अपनी इच्छित सभी कार्यक्षमता को इंजेक्ट करने में सक्षम हो सकते हैं। कुछ पुस्तकालय दूसरों की तुलना में इसे आसान बनाते हैं। या, मुझे कहना चाहिए, कुछ पुस्तकालय दूसरों की तुलना में इसे आसान बना देंगे।

  2. वर्ग लोड होने से पहले कक्षा को बदल दें। यह हमारे द्वारा पहले से ही चर्चा किए गए कोड के समान सामान्य पैटर्न का उपयोग करता है, सिवाय इसके कि आप रेट्रांसफॉर्मक्लास कॉलिंग के माध्यम से परिवर्तन को ट्रिगर न करें। बल्कि, आप क्लास लोड होने से पहले ट्रांस्फ़ॉर्म करने के लिए ClassFileTransformer को रजिस्टर करते हैं और फर्स्ट क्लास लोड होने पर आपका टारगेट क्लास संशोधित हो जाएगा। इस स्थिति में, आप अपनी पसंद के किसी भी वर्ग को संशोधित करने के लिए बहुत अधिक स्वतंत्र हैं, बशर्ते अंतिम उत्पाद अभी भी मान्य किया जा सकता है। पंच के लिए आवेदन को पीटना (यानी आवेदन को कक्षा में लोड करने से पहले अपना ClassFileTransformer पंजीकृत होना) सबसे अधिक संभावना है, जैसे कि jagagent की कमान की आवश्यकता होती है, हालांकि यदि आपके पास अपने आवेदन के जीवनचक्र का तंग नियंत्रण है, तो यह अधिक पारंपरिक में करना संभव है आवेदन परत कोड। जैसा कि मैंने कहा, आपको केवल यह सुनिश्चित करने की आवश्यकता है कि लक्ष्य वर्ग के लोड होने से पहले आपको ट्रांसफार्मर पंजीकृत करवाना चाहिए।

# 2 पर एक अन्य भिन्नता जो आप उपयोग करने में सक्षम हो सकते हैं, वह है एक नए क्लास लोडर का उपयोग करके किसी नए वर्ग का अनुकरण करना। यदि आप एक नया पृथक क्लास लोडर बनाते हैं, जो मौजूदा [लोडेड] वर्ग में नहीं आएगा, लेकिन [अनलोड] लक्ष्य वर्ग बाइट-कोड तक पहुंच है, तो आप अनिवार्य रूप से ऊपर दिए गए # 2 की आवश्यकताओं को दोहरा रहे हैं क्योंकि जेवीएम इस पर विचार करता है एक नया वर्ग बनने के लिए।

================ अद्यतन ==============

आपकी पिछली टिप्पणियों में, मुझे लगता है कि आप जहां हैं, वहां मैं थोड़ा सा ट्रैक कर चुका हूं। किसी भी दर पर, Oracle JDK 1.6 सबसे निश्चित रूप से रिट्रांसफॉर्म का समर्थन करता है। मैं ASM से बहुत परिचित नहीं हूं, लेकिन आपके द्वारा पोस्ट की गई अंतिम त्रुटि इंगित करती है कि ASM परिवर्तन ने किसी तरह वर्ग स्कीमा को संशोधित किया है जिसकी अनुमति नहीं है, इसलिए पुनर्परिवर्तन विफल रहा।

मुझे लगा कि एक काम करने का उदाहरण अधिक स्पष्टता लाएगा। ऊपर की कक्षाएँ (जैसे व्यक्ति नामक एक टेस्ट क्लास) here । संशोधनों / परिवर्धन के कुछ जोड़े हैं:

  • ट्रांसफॉर्मर सर्विस में अब TransformerService ऑपरेशन के 3 पैरामीटर हैं:
    1. बाइनरी क्लास का नाम
    2. साधन का नाम
    3. विधि हस्ताक्षर से मिलान करने के लिए एक [नियमित] अभिव्यक्ति। (यदि शून्य या रिक्त है, सभी हस्ताक्षरों से मेल खाता है)
    4. संशोधित ModifyMethodTest क्लास में Javassist का उपयोग करके वास्तविक Javassist संशोधन किया जाता है। सभी इंस्ट्रूमेंटेशन एक System.out.println जोड़ते हैं जो इस तरह दिखता है: -->Invoked method [com.heliosapm.shorthandexamples.Person.sayHello((I)V)]
  • AgentInstaller (जिसमें डेमो की मुख्य है) केवल एजेंट और ट्रांसफ़ॉर्म सेवा स्थापित करता है। (देव / डेमो उद्देश्यों के लिए आसान, लेकिन अभी भी अन्य JVM के साथ काम करेंगे)
  • एक बार जब एजेंट स्वयं स्थापित हो जाता है, तो मुख्य धागा Person उदाहरण बनाता है और बस लूप करता है, जो व्यक्ति के दो कहे जाने वाले तरीकों को बुलाता है।

रूपांतरित करने से पहले, वह आउटपुट निम्नानुसार दिखता है।

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]

व्यक्ति के पास 2 SayHello तरीके हैं, एक इंट लेता है, दूसरा एक स्ट्रिंग लेता है। (स्ट्रिंग एक लूप इंडेक्स के नकारात्मक को प्रिंट करता है)।

एक बार जब मैं AgentInstaller शुरू करता हूं, तो एजेंट स्थापित हो जाता है और व्यक्ति को लूप में आमंत्रित किया जाता है, मैं JCM को JConsole का उपयोग करके कनेक्ट करता हूं:

मैं ट्रांसफॉर्मर सर्विस एमबीएन पर नेविगेट करता हूं और ट्रांसफॉर्मक्लास ऑपरेशन शुरू करता हूं। मैं पूरी तरह से योग्य वर्ग [बाइनरी] नाम, उपकरण का नाम और रीजेक्स एक्सप्रेशन (I) V की आपूर्ति करता हूं , जो केवल SayHello पद्धति से मेल खाता है जो एक तर्क के रूप में एक इंट लेती है। (या मैं आपूर्ति कर सकता है । * , या सभी ओवरलोड से मेल खाने के लिए कुछ भी नहीं)। मैं ऑपरेशन को अंजाम देता हूं।

अब जब मैं रनिंग 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 bytecode संशोधन ने कोड को एक मौजूदा विधि में इंजेक्ट करने के अलावा कोई बदलाव नहीं किया है।

सही बात ?





cglib