zoukankan      html  css  js  c++  java
  • java agent 加载器织入——java.lang.instrument包 AOP,使用javassist

    https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487368&idx=1&sn=408c385d26083803e1a2a742bd301531&chksm=fa497039cd3ef92fcb8274f47130a90c3f9cf11b87b3b73054c20a7693021fb541a27bdf3c8f&mpshare=1&scene=1&srcid=0726C0dbs4R8kkHn1jG1zDK3&sharer_sharetime=1564316461171&sharer_shareid=b3ce2a829d6ac5a5c95b19a19559bab0&key=c1a84807be422668bf1a554d0cdfb3ecd67773641f76efc9a3fa110d4a94c319cf133ab80a19fe139053ae687bcfe6542c40e6f6a9ed8fd931c8aa10d07968a12690c6b87d1f463c027503089471a83c&ascene=0&uin=MTA2NzUxMDAyNQ%3D%3D&devicetype=iMac+MacBookAir6%2C2+OSX+OSX+10.10.5+build(14F2511)&version=11020012&lang=zh_CN&pass_ticket=uyNMv3dkU5lVbMU78UD3%2BXwjTGdkQzT8UBGxvcyQkuNJFvEWu4%2FLP%2Bvxng0jP1ai

    二、LTW(Load Time Weaving)

    其实,除了运行时织入切面的方式外,我们还有一种途径进行切面织入,它可以在类加载期通过字节码转换,进而将目标织入切入点(目标类),这种方式就是LTW,即静态代理(静待代理也被称作编译时增强,后面会有相关代码样例)。

    LTW在Java5的时候就被引入了,想要了解其原理,先要了解一个知识——Instrument包。

    由于assembly插件不支持pom中定义premain,故使用menifest

    Manifest-Version: 1.0
    Premain-Class: agent.MyTransformer
    Main-Class: agent.MyClient
    // 注意这里的空行

     放置于resources/META-INF/MANIFEST.MF

    pom修改为:

          <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
              <descriptorRefs>
                <descriptorRef>jar-with-dependencies</descriptorRef>
              </descriptorRefs>
              <archive>
                <!--<manifest>-->
                  <!--<mainClass>agent.AgentClient</mainClass>-->
                <!--</manifest>-->
                <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
              </archive>
            </configuration>
            <executions>
              <execution>
                <id>make-assembly</id>
                <phase>package</phase>
                <goals>
                  <goal>single</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
    

    主代码:

    import java.lang.instrument.ClassFileTransformer;
    import java.lang.instrument.IllegalClassFormatException;
    import java.lang.instrument.Instrumentation;
    import java.security.ProtectionDomain;
    
    /**
     * https://www.cnblogs.com/silyvin/p/11260965.html
     * Created by sunyuming on 19/7/28.
     */
    public class MyTransformer implements ClassFileTransformer {
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
    
            System.out.println("类 " + className);
    
            return new byte[0];
        }
    
        public static void premain(String args, Instrumentation instrumentation) {
            System.out.println("开始premain " + args);
            ClassFileTransformer classFileTransformer = new MyTransformer();
            instrumentation.addTransformer(classFileTransformer);
        }
    
        // cp target/MyTest-1.0-SNAPSHOT-jar-with-dependencies.jar ~/Documents/tool/jars/myagent.jar
        // java -javaagent:/Users/sunyuming/Documents/tool/jars/myagent.jar=sun -jar target/MyTest-1.0-SNAPSHOT-jar-with-dependencies.jar
    
        /**
         * 不能在此设置main函数,因为MyTransformer这个类会被提前加载,不会打印
         */
        public static void main(String [] fl) {
            System.out.println("my client trans");
        }
    }
    
    public class MyClient {
    
        public static void main(String [] fl) {
            System.out.println("my client 6");
        }
    }
    

    运行:

    java -javaagent:/Users/sunyuming/Documents/tool/jars/myagent.jar=sun -jar target/MyTest-1.0-SNAPSHOT-jar-with-dependencies.jar

    输出:

    开始premain sun
    类 java/lang/invoke/MethodHandleImpl
    类 java/lang/invoke/MethodHandleImpl$1
    类 java/lang/invoke/MethodHandleImpl$2
    类 java/util/function/Function
    类 java/lang/invoke/MethodHandleImpl$3
    类 java/lang/invoke/MethodHandleImpl$4
    类 java/lang/ClassValue
    类 java/lang/ClassValue$Entry
    类 java/lang/ClassValue$Identity
    类 java/lang/ClassValue$Version
    类 java/lang/invoke/MemberName$Factory
    类 java/lang/invoke/MethodHandleStatics
    类 java/lang/invoke/MethodHandleStatics$1
    类 sun/misc/PostVMInitHook
    类 sun/usagetracker/UsageTrackerClient
    类 java/util/concurrent/atomic/AtomicBoolean
    类 sun/usagetracker/UsageTrackerClient$1
    类 sun/usagetracker/UsageTrackerClient$4
    类 sun/usagetracker/UsageTrackerClient$3
    类 java/io/FileOutputStream$1
    类 sun/launcher/LauncherHelper
    类 sun/misc/IOUtils
    类 java/util/jar/JarVerifier
    类 java/security/CodeSigner
    类 java/util/jar/JarVerifier$3
    类 java/io/ByteArrayOutputStream
    类 agent/MyClient
    类 sun/launcher/LauncherHelper$FXHelper
    类 java/lang/Class$MethodArray
    类 java/lang/Void
    my client 6
    类 java/lang/Shutdown
    类 java/lang/Shutdown$Lock

    后续可以使用这种方式+asm或javaassist进行加载期aop

    javaassist:https://www.jianshu.com/p/b2d09a78678d  Javaagent技术初探

    asm:https://blog.csdn.net/conquer0715/article/details/51283610

    对比:https://blog.csdn.net/yczz/article/details/14497527  javasssit简单,性能差

    这里我们使用javassist测试:修改一下transorm:

        private ClassPool classPool = new ClassPool(true);
    
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
    
            if(className.equals("agent/MyClient")) {
                System.out.println("类 " + className);
                try {
                    CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
                    for(CtBehavior ctBehavior : ctClass.getDeclaredBehaviors()) {
                        if(ctBehavior.getLongName().equals("agent.MyClient.print()")) {
                            System.out.println("开始处理方法 " + ctBehavior.getLongName());
                            ctBehavior.insertBefore("System.out.println("前置aop");");
                            ctBehavior.insertAfter("System.out.println("后置aop");");
                        }
                    }
                    return ctClass.toBytecode();
    
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            } else
                return null;
    
        }
    
    public class MyClient {
    
        public static void main(String [] fl) {
            System.out.println("my client 6");
            MyClient myClient = new MyClient();
            myClient.print();
        }
    
        private void print() {
            System.out.println("自己的");
        }
    }
    
    // cp target/MyTest-1.0-SNAPSHOT-jar-with-dependencies.jar ~/Documents/tool/jars/myagent.jar
    // java -javaagent:/Users/sunyuming/Documents/tool/jars/myagent.jar=sun -jar target/MyTest-1.0-SNAPSHOT-jar-with-dependencies.jar

    输出:

    开始premain sun
    类 agent/MyClient
    开始处理方法 agent.MyClient.print()
    my client 6
    前置aop
    自己的
    后置aop

    ps:期间:No such Class : System.out

    https://stackoverflow.com/questions/26788724/javassist-cannotcompileexception-no-such-class-system-out

    You create the ClassPool like:

    ClassPool pool = new ClassPool();
    

    Which creates an empty ClassPool, not even the default Java classes from rt.jar. So there is no System class defined and compilation failes.

    By using:

    ClassPool pool = new ClassPool(true);
    

    You will get a ClassPool with the stuff from the classPath and system jars already added. System class is found and it compiles.

  • 相关阅读:
    lammps温度云图
    MS中石墨烯建模
    把网页上的内容保存为PDF格式
    lammps 计算热导率
    tomcat错误:严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.tomcat.util.modeler.BaseModelMBean.invoke Exception
    面向接口编程
    ms中使用AC模块建模时候,保持单个分子的键长 键角不发生变化。
    Python读取文件报错
    debug记录
    网络流
  • 原文地址:https://www.cnblogs.com/silyvin/p/11260965.html
Copyright © 2011-2022 走看看