javassist动态生成class

本文介绍Javassist库的基本概念及其在Java字节码操作中的应用。通过实例演示如何利用Javassist动态生成Java类并修改类结构,包括添加字段、方法及构造函数等。此外还展示了如何通过反射机制调用生成类的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

什么是javassist?

Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。

关于java字节码的处理,目前有很多工具,如bcel,asm。不过这些都需要直接跟虚拟机指令打交道。如果你不想了解虚拟机指令,可以采用javassist。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类
 

联想

 由上可知,javassist 可以用来动态生成class文件,并且JVM可以直接加载生成的class文件。

具体作用:

设计一个对接系统,通过动态模型的增删改触发业务系统相应服务的调用。模型增删改方法动态发布为WebService服务。WebService服务采用CXF发布,动态类生成采用Javassist。由于WebService服务类需要添加WebService相关注解。

实战演练

从0开始实战,首先我们认识最初级的由javassist生成class类并且修改保存
package com.bsoft.javassis;

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;


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

public class TestJavassis {

	/**
	 * @param args
	 * @author yuzg
	 * update time 2017-06-20
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		/**
		 * ClassPool是缓存CtClass对象的容器,所有的CtClass对象都在ClassPool中。
		 * 所以,CtClass对象很多时,ClassPool会消耗很大的内存,为了避免内存的消耗
		 * ,创建ClassPool对象时可以使用单例模式,
		 * 或者对于CtClass对象,调用detach方法将其从ClassPool中移除
		 * 在ClassPool源码中getDefault 是单例模式生成
		 * 
		 *  public static synchronized ClassPool getDefault() {
        if (defaultPool == null) {
            defaultPool = new ClassPool(null);
            defaultPool.appendSystemPath();
        }

        return defaultPool;
    }
		 */
		ClassPool classpool =ClassPool.getDefault();
		//创建类名
		CtClass ctClass = classpool.makeClass("com.bsoft.esb.IEsbInvoker1");
//		ctClass.stopPruning(true);
		try {
			//添加属性
	        ctClass.addField(CtField.make("private int age;", ctClass));
	        //添加setAge方法
	        ctClass.addMethod(CtMethod.make("public void setAge(int age){this.age = age;}", ctClass));
	        ctClass.addMethod(CtMethod.make("public int getAge(){return this.age;}", ctClass));
			byte[] byteArray = ctClass.toBytecode();
	        FileOutputStream output = new FileOutputStream("D:\\IEsbInvoker1.class");
	        output.write(byteArray);                                        
	        output.close();
	        
			/***
			 * 如果下面的判断和解冻方法不加会报错 
			 * java.lang.RuntimeException: com.bsoft.esb.IEsbInvoker1 class is frozen
			 * 原因解释如下:
			 *  当CtClass对象通过writeFile()、toClass()、toBytecode()转化为Class后,
			 *  Javassist冻结了CtClass对象,因此,JVM不允许再次加载Class文件,所以不允许对其修改。
			 */
	        if(ctClass.isFrozen()){
				ctClass.defrost();
			}
			 ctClass = classpool.get("com.bsoft.esb.IEsbInvoker1");
			System.out.println(ctClass);
			CtField param = new CtField(classpool.get("java.lang.String"), "name", ctClass);  
			 ctClass.addField(CtField.make("private java.lang.String sex;", ctClass));
		     ctClass.addField(CtField.make("private java.lang.String name;", ctClass));
		     ctClass.addMethod(CtNewMethod.setter("setName", param));  
		     ctClass.addMethod(CtNewMethod.getter("getName", param));  
//		     // 添加无参的构造体  
//	        CtConstructor cons = new CtConstructor(new CtClass[] {}, ctClass);  
//	        cons.setBody("{name = \"Brant\";}");  
//	        ctClass.addConstructor(cons);  
	     // 添加有参的构造体  
		    CtConstructor   cons = new CtConstructor(new CtClass[] {classpool.get("java.lang.String")}, ctClass);  
	        cons.setBody("{$0.name = $1;}");  
	        ctClass.addConstructor(cons);  
		    byteArray = ctClass.toBytecode();
	        output = new FileOutputStream("D:\\IEsbInvoker1.class");
	        output.write(byteArray);
	        output.close();

	        //ctClass转class后创建对象
	        Object o =ctClass.toClass().newInstance();
	        //这样写会报错:java.lang.ClassNotFoundException: com.bsoft.esb.IEsbInvoker1
	        /***
	         * 此时应该还在pool中
	         */
//	        Class.forName("com.bsoft.esb.IEsbInvoker1").newInstance();
	        //获取方法
	        Method methodSet = o.getClass().getMethod("setName", new Class[] {String.class});
	       //反射原理
	        methodSet.invoke(o, "Alen");
	        
	        Method getter = o.getClass().getMethod("getName");
	        System.out.println("name:"+getter.invoke(o, null));
		} catch (NotFoundException e) {
			System.out.println(e.getMessage());
			// TODO Auto-generated catch block
			e.printStackTrace();
		}catch (CannotCompileException e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		}
		catch (IOException e) {
			e.printStackTrace();
		}catch (Exception e) {
			e.printStackTrace();
		}
	}


$0, $1, $2, ...  代表的含义:

 $ 0代表的是 this,$ 1代表方法参数的第一个参数、$ 2代表方法参数的第二个参数,以此类推,$N代表是方法参数的第N个。例如:  
setName(String Name){
   $0.name=$1;
}
相当于 this.name=Name;




分别查看两次保存文件前后的class 文件 可用debug断点
首次保存生成的class文件:
package com.bsoft.esb;

public class IEsbInvoker1
{
  private int age;

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

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

修改后:






输出:
name:Alen
一个class 文件就生成好了。并且通过java反射机制可以明显看出,和jvm主动调用class类中的方法并无两样。稍后上传jar包资源文件

javassist.jar下载路径: 点击下载javassist

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值