Java动态性之Java字节码操作

会使用Javassist的基本操作

Java动态性的两种常见实现方式:

  • 字节码操作
  • 反射

运行时操作字节码有什么用:

  • 动态生成新的类
  • 动态改变某个类的结构(添加/删除/修改 新的属性/方法)

与反射相比的优势:

  • 比反射开销小,性能高
  • JAVAssist性能高于反射,低于ASM

常见的字节码操作类库

- BCEL
Byte Code Engineering Library (BCEL),这是Apache Software Foundation 的Jakarta项目的一
部分。BCEL是Java classworking广泛使用的一种框架,它可以让您深入JVM汇编语言进行类操作的细节。BCEL 与Javassist有不同的处理字节码方法,BCEL在实际的JVM指令层次上进行操作(BCEL拥有丰富的JVM指令级支持)而Javassist所强调的是源代码级别的工作。

- ASM
是一个轻量级java字节码操作框架 ,直接涉及 到VM底层的操作和指令

- CGLIB (Code Generation Library)
是一个强大的,高性能,高质量的Code生成类库,基于ASM实现。

- Javassist
是一个开源的分析、 编辑和创建Java字节码的类库。性能较ASM差,跟cglib差不多,但是使用简单。
很多开源框架都在使用它。

JAVAssist库

●javassist的最外层的API和JAVA的反射包中的API颇为类似。
● 它主要由CtClass, , CtMethod, ,以及CtField几个类组成。用以执行和JDK反射API中java.lang.Class, , java.lang.reflect.Method,,java.lang.reflect.Method .Field相同的操作。


要使用Javassist库需要上网下载响应的jar包,可以在 https://mvnrepository.com 这个网站上下载,挺好用的。

Javassist库的基本操作——生成一个新的类

package com.xyj.JAVAssist;

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

/**
 * 测试使用javassist生成一个新的类
 */
public class Demo01 {
	public static void main(String[] args) throws Exception {
		//获得类池
		ClassPool pool=ClassPool.getDefault();
		//获取CtClass
		CtClass cc = pool.makeClass("com.xyj.entity.Emp");
		
		//创建属性
		CtField field1 = CtField.make("private int empNo;",cc);
		CtField field2 = CtField.make("private String empName;",cc);
		cc.addField(field1);
		cc.addField(field2);
		
		//创建方法
		CtMethod method1 = CtMethod.make("public int getEmpNo(){return empNo;}",cc);
		CtMethod method2 = CtMethod.make("public void setEmpNo(int empNo){empNo=empNo;}",cc);//$0代表this关键字  $1代表第一个参数  $2代表第二个参数
		cc.addMethod(method1);
		cc.addMethod(method2);
		
		//添加构造器
		CtConstructor constructor1=new CtConstructor(new CtClass[] {CtClass.intType,pool.get("java.lang.String")}, cc);
		constructor1.setBody("{empNo=$1;empName=$2;}");
		CtConstructor constructor2=new CtConstructor(new CtClass[] {}, cc);
		constructor2.setBody("{}");
		cc.addConstructor(constructor1);
		cc.addConstructor(constructor2);
		
		cc.writeFile("f:/MyJava");//将上面构建好的类写入到f:/MyJava中
		System.out.println("生成类成功");
	}
}

经过XJad反编译工具反编译我们刚刚动态编译好的class文件,如下:
Java动态性之Java字节码操作_第1张图片

JAVAssist库的API详解

Java动态性之Java字节码操作_第2张图片

测试Javassist操作类、方法,属性,构造器,注解

package com.xyj.JAVAssist;

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

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

/**
 *  测试javassist的API
 *  javassist发明人 Chiba
 */
public class Demo02 {
	
	/**
	 * 处理类的基本用法
	 * @throws Exception 
	 */
	public static void test01() throws Exception {
		ClassPool pool=ClassPool.getDefault();
		CtClass cc = pool.get("com.xyj.JAVAssist.Emp");//获取已有的类的信息
		
		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());//获得实现的接口
		
	}
	
	
	/**
	 * 处理方法的基本用法 测试产生新的方法
	 * @throws Exception 
	 */
	public static void test02() throws Exception {
		ClassPool pool=ClassPool.getDefault();
		CtClass cc = pool.get("com.xyj.JAVAssist.Emp");//获取已有的类的信息
		
		//CtMethod method = CtNewMethod.make("public int add(int a,int b){return a+b;}",cc);
		
		CtMethod m=new CtMethod(CtClass.intType,"add",
				new CtClass[] {CtClass.intType,CtClass.intType},cc);
		m.setBody("{System.out.println(\"add Method in here.\");return $1+$2;}");//$0代表this关键字  $1代表第一个参数   $2代表第二个参数
		m.setModifiers(Modifier.PUBLIC);
		
		cc.addMethod(m);
		
		//通过反射调用新生成的方法
		Class<?> clazz=cc.toClass();
		Object obj = clazz.newInstance();//通过调用Emp的无参构造,创建新的对象
		Method method=clazz.getDeclaredMethod("add",int.class,int.class);
		Object result = method.invoke(obj,200,300);
		System.out.println(result);
		
	}
	
	
	/**
	 * 处理现有方法 在方法里面的前后插入代码
	 * @throws Exception 
	 */
	public static void test03() throws Exception {
		ClassPool pool=ClassPool.getDefault();
		CtClass cc = pool.get("com.xyj.JAVAssist.Emp");//获取已有的类的信息
		
		CtMethod method = cc.getDeclaredMethod("sayHello",new CtClass[] {pool.get("java.lang.String")});
		method.insertBefore("System.out.println(\"上午好\");");
		method.insertAt(9,"System.out.println(\"好烦哦\");");//在第几行插入代码
		method.insertAfter("System.out.println(\"下午好\");");
		
		Class<?> class1 = cc.toClass();
		Object object = class1.newInstance();
		Method method2 = class1.getDeclaredMethod("sayHello",String.class);
		method2.invoke(object,"李四");
		
	}
	
	
	/**
	 * 处理属性
	 */
	public static void test04()  throws Exception {
		ClassPool pool=ClassPool.getDefault();
		CtClass cc = pool.get("com.xyj.JAVAssist.Emp");
		
		//CtField f=CtField.make("private int empNo",cc);
		
		CtField field=new CtField(CtClass.intType,"empAge",cc);
		field.setModifiers(Modifier.PRIVATE);
		cc.addField(field,"1000");//"1000"是默认值 可不写
		
		//CtField declaredField = cc.getDeclaredField("empAge");//获取指定的属性
		
		//增加响应的get set方法
		CtNewMethod.getter("getEmpAge",field);
		CtNewMethod.setter("setEmpAge",field);
		
	}
	
	
	/**
	 * 处理构造器
	 * @throws Exception
	 */
	public static void test05() throws Exception{
		ClassPool pool=ClassPool.getDefault();
		CtClass cc = pool.get("com.xyj.JAVAssist.Emp");
		
		CtConstructor[] constructors = cc.getConstructors();
		for (CtConstructor c : constructors) {
			System.out.println(c.getLongName());
		}
	}
	
	
	/**
	 * 处理注解
	 */
	public static void test06() throws Exception{
		CtClass cc = ClassPool.getDefault().get("com.xyj.JAVAssist.Emp");
		Object[] annotations = cc.getAnnotations();
		Author e=(Author)annotations[0];
		String name = e.name();
		int year = e.year();
		System.out.println("name :"+name+",year :"+year);
	}
	
	
	public static void main(String[] args) throws Exception {
	
		//test01();
		//test02();
		//test03();
		//test04();
		//test05();
		test06();
		
	}
}

Author注解如下:

package com.xyj.JAVAssist;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Author {
	String name();
	int year();
}

Emp类如下:

package com.xyj.JAVAssist;

@Author(name="xyj",year=2019)
public class Emp {
	
	private int empNo;
	private String empName;
	
	public void sayHello(String user) {
		System.out.println(user+"你好呀");
	}
	
	public int getEmpNo() {
		return empNo;
	}
	public void setEmpNo(int empNo) {
		this.empNo = empNo;
	}
	public String getEmpName() {
		return empName;
	}
	public void setEmpName(String empName) {
		this.empName = empName;
	}
	
	public Emp() {
		
	}
	public Emp(int empNo, String empName) {
		this.empNo = empNo;
		this.empName = empName;
	}
	
}

你可能感兴趣的:(Java,注解_反射_字节码_类加载机制)