javaagent demo程序(使用javaagent实现字节码层面的代码修改)

javaagent demo程序(使用javaagent实现字节码层面的代码修改)

本文主要讲解一下内容:
1、javaagent的作用。
2、一个可用的javaagent demo程序。
3、引出类似的工具bytebuddy和asm。

好,下面上货。
一、首先说一下javaagent 的作用。javaagent是一种能够在不影响正常编译的情况下,修改字节码。java作为一种强类型的语言,不通过编译就不能能够进行jar包的生成。而有了javaagent技术,就可以在字节码这个层面对类和方法进行修改。同时,也可以把javaagent理解成一种代码注入的方式。但是这种注入比起spring的aop更加的优美。

二、一个javaagent demo程序
1、首先创建agent。作为agent的jar包必须有两个要求。
一个是必须实现premain方法,另一个是必须在MANIFEST.MF文件中有Premain-Class。
先看一下agent的实现。
package com.xueyou.demo.agent;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;

public class FirstAgent implements ClassFileTransformer {
    public final String injectedClassName = "com.xueyou.agentdemo.App";
    public final String methodName = "hello";

    public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        className = className.replace("/", ".");
//        System.out.println(className);
        if (className.equals(injectedClassName)) {
            CtClass ctclass = null;
            try {
                ctclass = ClassPool.getDefault().get(className);// 使用全称,用于取得字节码类<使用javassist>
                CtMethod ctmethod = ctclass.getDeclaredMethod(methodName);// 得到这方法实例
                ctmethod.insertBefore("System.out.println(11111111);");
                return ctclass.toBytecode();
            } catch (Exception e) {
                System.out.println(e.getMessage());
                e.printStackTrace();
            }
        }
        return null;
    }
}


然后需要在premain中对这个FirstAgent进行加载,具体代码如下:
package com.xueyou.demo;

import com.xueyou.demo.agent.FirstAgent;

import java.lang.instrument.Instrumentation;

/**
 * Hello world!
 */
public class App {
    public static void premain(String agentOps, Instrumentation inst) {
        System.out.println("=========premain方法执行========");
        System.out.println(agentOps);
        // 添加Transformer
        inst.addTransformer(new FirstAgent());
    }
}

下面比较重要的是pom文件中如何对jar包进行build,因为需要自动的在MANIFEST.MF文件中添加Premain-Class,所以这里需要在pom中添加插件并且指定premain-class。

        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                
                    1.8
                    1.8
                    utf-8
                
            
            
                org.apache.maven.plugins
                maven-shade-plugin
                3.0.0
                
                    
                        package
                        
                            shade
                        
                        
                            
                                
                                    
                                        com.xueyou.demo.App
                                    
                                
                            
                        
                    
                
            
        
    

这样打包之后能够在jar包内看到对应的premain-class。
Manifest-Version: 1.0
Premain-Class: com.xueyou.demo.App
Archiver-Version: Plexus Archiver
Built-By: wuxueyou
Created-By: Apache Maven 3.3.9
Build-Jdk: 1.8.0_101

2、现在我们编写这个FirstAgent要拦截的类(也可以说是要加强的类)
package com.xueyou.agentdemo;

/**
 * Hello world!
 */
public class App {
    public static void main(String[] args) {
        hello();
    }

    public static void hello() {
        System.out.println("this is agent-demo output");
    }
}


3、编写好程序后,在运行的时候需要指定javaagent。
这里有两种方式指定javaagent:
1)使用命令行:java -javaagent:XXX.jar  ddd.jar
2)使用idea的vm option选项。


这里需要配置的就是-javaagent:agent-1.0.-SNAPSHOT.jar即可。

4、运行查看结果:



三、虽然javaagent可以完成对类的拦截和增强,但是在使用的时候还是有些困难,需要一直中string的方式进行代码的编写,而且调试的时候也不是很方便。所以,如果允许的话,可以使用bytebuddy和asm。

你可能感兴趣的:(java)