[19/04/20-星期六] Java的动态性_字节码操作(Javassist类库(jar包),assist:帮助、援助)...

本文详细介绍使用Javassist库动态生成Java字节码的过程,包括创建类、添加字段、方法及构造器,并演示如何修改现有类的方法和字段,以及读取注解信息。

一、概念

【基本】

/**
 * 
 */
package cn.sxt.jvm;


import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;

public class Test_0420_Javassist {
    public static void main(String[] args) throws Exception {
        ClassPool pool =ClassPool.getDefault();//获得一个类池
        CtClass cc=pool.makeClass("cn.sxt.jvm.Test_0420_Emp2");//cc代表要要创建的一个类 ct:compile time(编译和运行)
        
        //创建属性    注意""里边的;和{ }问题,跟在编译器里边写代码是一样的
        CtField field=CtField.make("private int empNo;", cc);//直接在这个类中书写另一个类的源码
        CtField field2=CtField.make("private String empName;", cc);
        cc.addField(field);
        cc.addField(field2);
        
        //创建方法
        CtMethod method=CtMethod.make("public int getEmpNo(){return empNo;}", cc);
        CtMethod method2=CtMethod.make("public void setEmpNo(int empNo){this.empNo=empNo;}", cc);
        cc.addMethod(method);
        cc.addMethod(method2);
        
        //添加构造器
        CtConstructor constructor=new CtConstructor(new CtClass[] {CtClass.intType,
                pool.get("java.lang.String")}, cc);//有参数构造器
    
        constructor.setBody("{this.empNo=empNo;this.empName=empName;}");
        cc.addConstructor(constructor);
        
        cc.writeFile("F:/MyEclipse/WorkSpace");//把写好的类写出到文件,写出去的是.class文件,不认识!若要认识它需要反编译工具XJad
        System.out.println("生成类成功!");
        
        
    }
}

【示例】

 1 /***
 2  * 一个示例
 3  */
 4 package cn.sxt.jvm;
 5 
 6 public class Test_0420_Emp {
 7     private int id;
 8     private String name;
 9 
10     public void print(int a) {
11         System.out.println("输出:"+a);
12         
13     }
14 
15     public Test_0420_Emp() {
16 
17     }
18     
19     public Test_0420_Emp(int id, String name) {
20         super();
21         this.id = id;
22         this.name = name;
23     }
24 
25 
26     public int getId() {
27         return id;
28     }
29 
30     public void setId(int id) {
31         this.id = id;
32     }
33     
34     public String getName() {
35         return name;
36     }
37     
38     public void setName(String name) {
39         this.name = name;
40     }
41     
44 }

1 package cn.sxt.jvm;
2 
3 public @interface Test_0420_Annotation {
4     String name();
5     int year();
6     
7 }

【API】

/***
 * 测试API
 */
package cn.sxt.jvm;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;

import javax.security.sasl.AuthorizeCallback;

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



public class Test_0420_Javassist2  {
    public static void test01() throws Exception{
        ClassPool pool=ClassPool.getDefault();
        CtClass cc=pool.get("cn.sxt.jvm.Test_0420_Emp");//获得一个已经存在的类去操作,不要带"package"," "中不要有空格

        byte[] bytes=cc.toBytecode();
        System.out.println(Arrays.toString(bytes));//输出的字节码的内容编码
        System.out.println(cc.getName());//获得完整类名
        System.out.println(cc.getSimpleName());//获得简要类名
        System.out.println(cc.getSuperclass());//获得父类类名
        System.out.println(cc.getInterfaces());//获得接口

    }
    public static void test02() throws Exception {
        ClassPool pool=ClassPool.getDefault();
        CtClass cc=pool.get("cn.sxt.jvm.Test_0420_Emp");

        //CtMethod method=CtMethod.make("public int add(int a,int b){return a+b;}", cc);
        CtMethod method=new CtMethod(CtClass.intType, "add",new CtClass[]{CtClass.intType,CtClass.intType}
        ,cc);//4个参数含义:返回值类型,方法名字,传入参数类型,类的对象cc.   new CtClass[]代表数组,固定套路
        method.setModifiers(Modifier.PUBLIC); //设置方法的修饰,公开or私有  Modifiers:修饰符
        method.setBody("{System.out.println(\" 求和结果为:\"); return $1+$2;}");//设置方法的结构体。 这3行与上边的一行效果一样
        cc.addMethod(method);//$1和$2代表2个形参,是占位符 $0代表this关键字

        //通过反射调用新建的方法
        Class clz=cc.toClass();
        Object obj=clz.newInstance();//通过调用Test_0420_Emp的无参构造器去创建新的Test_0420_Emp对象
        Method m=clz.getDeclaredMethod("add",int.class,int.class);
        Object result=m.invoke(obj, 200,300);
        System.out.println(result);
    }
    //修改已有的方法信息
    public static void test03() throws Exception {
        ClassPool pool=ClassPool.getDefault();
        CtClass cc=pool.get("cn.sxt.jvm.Test_0420_Emp");

        CtMethod method=cc.getDeclaredMethod("print",new CtClass[]{CtClass.intType});
        method.insertAt(11," System.out.println(\"开始\"); "); //在第11行前加代码,第11行代码是:System.out.println("输出:"+a); 
        method.insertBefore("System.out.println(\"输入:\"+$1);");//修改方法,把" "中的代码加到整个方法体的前边(包括新插入的第11行代码),$1形参占位符
        method.insertAfter("System.out.println(\"结束!\");");//修改方法,方法体后边加代码


        //通过反射调用新建的方法
        Class clz=cc.toClass();
        Object obj=clz.newInstance();//通过调用Test_0420_Emp的无参构造器去创建新的Test_0420_Emp对象
        Method m=clz.getDeclaredMethod("print",int.class);
        m.invoke(obj, 200);
    }

    //修改已有的属性
    public static void test04() throws Exception {
        ClassPool pool=ClassPool.getDefault();
        CtClass cc=pool.get("cn.sxt.jvm.Test_0420_Emp");
        //CtField field=CtField.make("private int age=18;", cc); 新建属性方式之一
        CtField field=new CtField(CtClass.intType, "age",cc);//方式之二
        field.setModifiers(Modifier.PRIVATE);
        //cc.addField(field,"18");//默认值18
        cc.addField(field);

        //cc.getDeclaredField("id");//获取相应属性

        cc.addMethod(CtNewMethod.getter("getAge", field));//也可以通过这种方式增加属性的get,set方法
        cc.addMethod(CtNewMethod.setter("setAge", field));

    }

    //构造器,也可以在构造器前后加代码
    public static void test05() throws Exception {
        ClassPool pool=ClassPool.getDefault();
        CtClass cc=pool.get("cn.sxt.jvm.Test_0420_Emp");

        CtConstructor[] cs=cc.getConstructors();//得到所有的构造器
        for (CtConstructor tempConstructor : cs) {
            System.out.println(tempConstructor.getLongName());

        }
    }
    //读取注解
    public static void test06() throws Exception {
        ClassPool pool=ClassPool.getDefault();
        CtClass cc=pool.get("cn.sxt.jvm.Test_0420_Emp");
        Object[] all=cc.getAnnotations();
        Test_0420_Annotation a=(Test_0420_Annotation)all[0];//0 表示就一个注解,编译器可以找到 
        String name=a.name();//读取注解
        int year=a.year();
        System.out.println(name+"->"+year);

    }

    public static void main(String[] args) throws Exception  {
        //test01();
        //test02();
        //test03();
        test06();

    }

}

 

转载于:https://www.cnblogs.com/ID-qingxin/p/10732621.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值