Javassist 字节码 反编译 案例 MD

Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 [email protected]

目录

基本使用

原始类

package com.bqt.test;

public class Person {

    public int hello(String s) {
        return s.length();
    }

    public String hello2(String s) {
        return s;
    }
}

创建新类

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("com.bqt.test.User");

//创建属性
cc.addField(CtField.make("private int id;", cc));
cc.addField(CtField.make("private String name;", cc));

//创建方法
cc.addMethod(CtMethod.make("public String getName(){return name;}", cc));
cc.addMethod(CtMethod.make("public void setName(String name){this.name = name;}", cc));

//添加构造器
CtConstructor constructor = new CtConstructor(new CtClass[] { CtClass.intType, pool.get("java.lang.String") }, cc);
constructor.setBody("{this.id=id;this.name=name;}");
cc.addConstructor(constructor);

cc.writeFile("d:/test");

生成的类经反编译后的源码为:

package com.bqt.test;

public class User {
    private int id;
    private String name;

    public User(int paramInt, String paramString) {
        this.id = this.id;
        this.name = this.name;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String paramString) {
        this.name = paramString;
    }
}

获取类信息

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.bqt.test.Person");

byte[] bytes = cc.toBytecode();//得到字节码  
System.out.println(bytes.length);
System.out.println(cc.getName());//获取类名  
System.out.println(cc.getSimpleName());//获取简要类名  
System.out.println(cc.getSuperclass().getName());//获取父类  
System.out.println(Arrays.toString(cc.getInterfaces()));//获取接口  

for (CtConstructor con : cc.getConstructors()) {//获取构造方法
    System.out.println("构造方法 "+con.getLongName());
}

for (CtMethod method : cc.getMethods()) {//获取方法
    System.out.println(method.getLongName());
}

打印内容:

562
com.bqt.test.Person
Person
java.lang.Object
[]
构造方法 com.bqt.test.Person()
java.lang.Object.equals(java.lang.Object)
java.lang.Object.finalize()
com.bqt.test.Person.hello2(java.lang.String)
java.lang.Object.toString()
java.lang.Object.getClass()
java.lang.Object.notifyAll()
java.lang.Object.hashCode()
java.lang.Object.wait()
java.lang.Object.notify()
com.bqt.test.Person.hello(java.lang.String)
java.lang.Object.wait(long)
java.lang.Object.wait(long,int)
java.lang.Object.clone()

添加方法

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.bqt.test.Person");

//方式一
CtMethod cm1 = CtMethod.make("public int add1(int a, String b){return a+b.length();}", cc);//第一种方式,完整的方法以字符串形式传递过去
cc.addMethod(cm1); //cc.removeMethod(cm3) 删除一个方法

//方式二
CtClass[] parameters = new CtClass[] { CtClass.intType, pool.get("java.lang.String") };
CtMethod cm2 = new CtMethod(CtClass.intType, "add2", parameters, cc);//第二种方式,返回值类型,方法名,参数,对象  
cm2.setModifiers(Modifier.PUBLIC);//访问范围  
cm2.setBody("{return $1+$2.length();}");//方法体
cc.addMethod(cm2);

//通过反射调用方法  
Class clazz = cc.toClass(); //注意,如果使用 JDK9 以下的JDK ,同时使用 3.20.0-GA 以上版本的 Javassist,会报 StackWalker 异常
Object obj = clazz.newInstance();//通过调用无参构造器,生成新的对象

Method m1 = clazz.getDeclaredMethod("add1", int.class, String.class);
System.out.println(m1.invoke(obj, 1, "包青天"));

Method m2 = clazz.getDeclaredMethod("add2", int.class, String.class);
System.out.println(m2.invoke(obj, 2, "包青天"));

cc.writeFile("D:/test");//保存到指定位置

生成的类经反编译后的源码为:

package com.bqt.test;

public class Person {
    public int hello(String s) {
        return s.length();
    }

    public String hello2(String s) {
        return s;
    }

    public int add1(int paramInt, String paramString) {
        return paramInt + paramString.length();
    }

    public int add2(int paramInt, String paramString) {
        return paramInt + paramString.length();
    }
}

修改方法

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.bqt.test.Person");

//方式一
CtMethod cm = cc.getDeclaredMethod("hello", new CtClass[] { pool.get("java.lang.String") });
cm.insertBefore("System.out.println(\"调用前2\");");//调用前
cm.insertBefore("System.out.println(\"调用前1\");");
cm.insertAt(20094, "System.out.println(\"在指定行插入代码\");");//貌似行号胡乱写也可以
cm.insertAfter("System.out.println(\"调用后1\");");//调用后
cm.insertAfter("System.out.println(\"调用后2\");");//调用后

//方式二
CtMethod cm2 = cc.getDeclaredMethod("hello2", new CtClass[] { pool.get("java.lang.String") });
cm2.setBody("{" + // 你只需要正常写代码逻辑就可以了,复制过来时,一些IDE,比如AS会自动帮你添加转义字符
        "if ($1 == null) {\n" + //$0代表的是this,$1代表方法参数的第一个参数、$2代表方法参数的第二个参数
        "\treturn \"\";\n" + //
        "}\n" + //
        "return  \"你好:\" + $1;" + //
        "}");

//通过反射调用方法  
Class clazz = cc.toClass();
Object obj = clazz.newInstance();

Method m = clazz.getDeclaredMethod("hello", String.class);
System.out.println(m.invoke(obj, "包青天"));

Method m2 = clazz.getDeclaredMethod("hello2", String.class);
System.out.println(m2.invoke(obj, "包青天"));

cc.writeFile("D:/test");//保存到指定位置

生成的类经反编译后的源码为:

package com.bqt.test;

import java.io.PrintStream;

public class Person {
    public int hello(String s) {
        System.out.println("调用前1");
        System.out.println("调用前2");
        System.out.println("在指定行插入代码");
        int i = s.length();
        System.out.println("调用后1");
        int j = i;
        System.out.println("调用后2");
        return j;
    }

    public String hello2(String paramString) {
        if (paramString == null) {
            return "";
        }
        return "你好:" + paramString;
    }
}

添加属性

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.bqt.test.Person");

//方式一
CtField cf = new CtField(CtClass.intType, "age", cc);
cf.setModifiers(Modifier.PRIVATE);
cc.addField(cf);
cc.addMethod(CtNewMethod.getter("getAge", cf));
cc.addMethod(CtNewMethod.setter("setAge", cf));

//方式二
CtField cf2 = CtField.make("private String name;", cc);
cc.addField(cf2);
cc.addMethod(CtNewMethod.getter("getName", cf2)); //快捷的添加get/set方法
cc.addMethod(CtNewMethod.setter("setName", cf2));

Class clazz = cc.toClass();
Object obj = clazz.newInstance();
System.out.println(clazz.getDeclaredField("age"));
System.out.println(clazz.getDeclaredField("name"));

clazz.getDeclaredMethod("setAge", int.class).invoke(obj, 16);
Object resutl = clazz.getDeclaredMethod("getAge", null).invoke(obj, null);
System.out.println(resutl);

cc.writeFile("D:/test");//保存到指定位置

生成的类经反编译后的源码为:

package com.bqt.test;

public class Person {
    private int age;
    private String name;

    public int hello(String s) {
        return s.length();
    }

    public String hello2(String s) {
        return s;
    }

    public int getAge() {
        return this.age;
    }

    public void setAge(int paramInt) {
        this.age = paramInt;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String paramString) {
        this.name = paramString;
    }
}

实现动态代理

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.bqt.test.Person");

ProxyFactory factory = new ProxyFactory();//代理类工厂
factory.setSuperclass(cc.toClass());//设置父类,ProxyFactory将会动态生成一个类,继承该父类

//设置过滤器,判断哪些方法调用需要被拦截
factory.setFilter(new MethodFilter() {
    @Override
    public boolean isHandled(Method m) {
        return m.getName().startsWith("hello");
    }
});

Class<?> clazz = factory.createClass();//创建代理类型
Person proxy = (Person) clazz.newInstance();//创建代理实例

//设置代理处理方法
((ProxyObject) proxy).setHandler(new MethodHandler() {
    @Override
    public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
        System.out.println(thisMethod.getName() + "被调用了");
        try {
            Object ret = proceed.invoke(self, args);//thisMethod为被代理方法,proceed为代理方法,self为代理实例,args为方法参数
            System.out.println("返回值: " + ret);
            return ret;
        } finally {
            System.out.println(thisMethod.getName() + "调用完毕");
        }
    }
});

//测试
proxy.hello("包青天");
System.out.println(proxy.getClass().getName()); //com.bqt.test.Person_$$_jvstee7_0

打印日志:

hello被调用了
返回值: 3
hello调用完毕
com.bqt.test.Person_$$_jvstee7_0

2019-1-7

猜你喜欢

转载自www.cnblogs.com/baiqiantao/p/10235049.html