0
点赞
收藏
分享

微信扫一扫

java字节码编程技术(8/10) -Javassist

Javassist

这个库和asm经常使用,它的性能稍差一点

<dependency>

<groupId>org.javassist</groupId>

<artifactId>javassist</artifactId>

<version>3.27.0-GA</version>

</dependency>

获取一个类加载器(ClassLoader),以加载指定的.jar或.class文件

private ClassLoader getLocaleClassLoader() throws Exception {

List<URL> classPathURLs = new ArrayList<>();

// 加载.class文件路径

File classesPath = new File("com.assist.TestHelloWorld2");

classPathURLs.add(classesPath.toURI().toURL());


// 获取所有的jar文件

File libPath = new File("com.assist.TestHelloWorld2");

File[] jarFiles = libPath.listFiles(new FilenameFilter() {

@Override

public boolean accept(File dir, String name) {

return name.endsWith(".jar");

}

});


// 将jar文件路径写入集合

for (File jarFile : jarFiles) {

classPathURLs.add(jarFile.toURI().toURL());

}


// 实例化类加载器

return new URLClassLoader(classPathURLs.toArray(new URL[classPathURLs.size()]));

}

获取类的类型信息

public static void test() throws NotFoundException {

// 获取默认类型池对象

ClassPool classPool = ClassPool.getDefault();


// 获取指定的类型

CtClass ctClass = classPool.get("java.lang.String");


System.out.println(ctClass.getName()); // 获取类名

System.out.println("\tpackage " + ctClass.getPackageName()); // 获取包名

System.out.print("\t" + Modifier.toString(ctClass.getModifiers()) + " class " + ctClass.getSimpleName()); // 获取限定符和简要类名

System.out.print(" extends " + ctClass.getSuperclass().getName()); // 获取超类

// 获取接口

if (ctClass.getInterfaces() != null) {

System.out.print(" implements ");

boolean first = true;

for (CtClass c : ctClass.getInterfaces()) {

if (first) {

first = false;

} else {

System.out.print(", ");

}

System.out.print(c.getName());

}

}

System.out.println();

}

修改类的信息

public void test() throws Exception {

// 获取本地类加载器

ClassLoader classLoader = getLocaleClassLoader();

// 获取要修改的类

Class<?> clazz = classLoader.loadClass("edu.alvin.reflect.TestLib");


// 实例化类型池对象

ClassPool classPool = ClassPool.getDefault();

// 设置类搜索路径

classPool.appendClassPath(new ClassClassPath(clazz));

// 从类型池中读取指定类型

CtClass ctClass = classPool.get(clazz.getName());


// 获取String类型参数集合

CtClass[] paramTypes = {classPool.get(String.class.getName())};

// 获取指定方法名称

CtMethod method = ctClass.getDeclaredMethod("show", paramTypes);

// 赋值方法到新方法中

CtMethod newMethod = CtNewMethod.copy(method, ctClass, null);

// 修改源方法名称

String oldName = method.getName() + "$Impl";

method.setName(oldName);


// 修改原方法

newMethod.setBody("{System.out.println(\"执行前\");" + oldName + "($$);System.out.println(\"执行后\");}");

// 将新方法添加到类中

ctClass.addMethod(newMethod);


// 加载重新编译的类

clazz = ctClass.toClass(); // 注意,这一行会将类冻结,无法在对字节码进行编辑

// 执行方法

clazz.getMethod("show", String.class).invoke(clazz.newInstance(), "hello");

ctClass.defrost(); // 解冻一个类,对应freeze方法

}

获取方法名称

public void test1() throws Exception {

// 获取本地类加载器

ClassLoader classLoader = getLocaleClassLoader();

// 获取要修改的类

Class<?> clazz = classLoader.loadClass("edu.alvin.reflect.TestLib");


// 实例化类型池

ClassPool classPool = ClassPool.getDefault();

classPool.appendClassPath(new ClassClassPath(clazz));

CtClass ctClass = classPool.get(clazz.getName());


// 获取方法

CtMethod method = ctClass.getDeclaredMethod("show",new CtClass[]{});

// 判断是否为静态方法

int staticIndex = Modifier.isStatic(method.getModifiers()) ? 0 : 1;


// 获取方法的参数

MethodInfo methodInfo = method.getMethodInfo();

CodeAttribute codeAttribute = methodInfo.getCodeAttribute();

LocalVariableAttribute localVariableAttribute = (LocalVariableAttribute)codeAttribute.getAttribute(LocalVariableAttribute.tag);


for (int i = 0; i < method.getParameterTypes().length; i++) {

System.out.println("第" + (i + 1) + "个参数名称为: " + localVariableAttribute.variableName(staticIndex + i));

}

}

动态创建类

public void test() throws Exception {

ClassPool classPool = ClassPool.getDefault();


// 创建一个类

CtClass ctClass = classPool.makeClass("edu.alvin.reflect.DynamiClass");

// 为类型设置接口

//ctClass.setInterfaces(new CtClass[] {classPool.get(Runnable.class.getName())});


// 为类型设置字段

CtField field = new CtField(classPool.get(String.class.getName()), "value", ctClass);

field.setModifiers(Modifier.PRIVATE);

// 添加getter和setter方法

ctClass.addMethod(CtNewMethod.setter("setValue", field));

ctClass.addMethod(CtNewMethod.getter("getValue", field));

ctClass.addField(field);


// 为类设置构造器

// 无参构造器

CtConstructor constructor = new CtConstructor(null, ctClass);

constructor.setModifiers(Modifier.PUBLIC);

constructor.setBody("{}");

ctClass.addConstructor(constructor);

// 参数构造器

constructor = new CtConstructor(new CtClass[] {classPool.get(String.class.getName())}, ctClass);

constructor.setModifiers(Modifier.PUBLIC);

constructor.setBody("{this.value=$1;}");

ctClass.addConstructor(constructor);


// 为类设置方法

CtMethod method = new CtMethod(CtClass.voidType, "run", null, ctClass);

method.setModifiers(Modifier.PUBLIC);

method.setBody("{System.out.println(\"执行结果\" + this.value);}");

ctClass.addMethod(method);


// 加载和执行生成的类

Class<?> clazz = ctClass.toClass();

Object obj = clazz.newInstance();

clazz.getMethod("setValue", String.class).invoke(obj, "hello");

clazz.getMethod("run").invoke(obj);


obj = clazz.getConstructor(String.class).newInstance("OK");

clazz.getMethod("run").invoke(obj);

}

创建代理类

public class TestProxy {

private String name;

private String value;


public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getValue() {

return value;

}

public void setValue(String value) {

this.value = value;

}

}

public static void test() throws Exception {

// 实例化代理类工厂

ProxyFactory factory = new ProxyFactory();


//设置父类,ProxyFactory将会动态生成一个类,继承该父类

factory.setSuperclass(TestProxy.class);


//设置过滤器,判断哪些方法调用需要被拦截

factory.setFilter(new MethodFilter() {

@Override

public boolean isHandled(Method m) {

return m.getName().startsWith("get");

}

});


Class<?> clazz = factory.createClass();

TestProxy proxy = (TestProxy) clazz.newInstance();

((ProxyObject)proxy).setHandler(new MethodHandler() {

@Override

public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {

//拦截后前置处理,改写name属性的内容

//实际情况可根据需求修改

System.out.println(thisMethod.getName() + "被调用");

try {

Object ret = proceed.invoke(self, args);

System.out.println("返回值: " + ret);

return ret;

} finally {

System.out.println(thisMethod.getName() + "调用完毕");

}

}

});


proxy.setName("Alvin");

proxy.setValue("1000");

proxy.getName();

proxy.getValue();

}

创建一个类并添加方法

public class TestProxy {

public static void main(String[] args) throws NotFoundException,

IOException, CannotCompileException, InstantiationException,

IllegalAccessException, SecurityException, NoSuchMethodException,

IllegalArgumentException, InvocationTargetException {

// 用于取得字节码类,必须在当前的classpath中,使用全称

ClassPool pool = ClassPool.getDefault();

/**

* makeClass() cannot create a new interface; makeInterface() in

* ClassPool can do. Member methods in an interface can be created with

* abstractMethod() in CtNewMethod. Note that an interface method is an

* abstract method.

*/

CtClass ccClass = pool.makeClass("Point");

String bodyString = "{System.out.println(\"Call to method \");}";

//为新创建的类新加一个方法execute,无任何参数

CtMethod n1 = CtNewMethod.make(CtClass.voidType, "execute", null, null,

bodyString, ccClass);

ccClass.addMethod(n1);

//新加第二个方法

bodyString = "public Integer getNumber(Integer num);";

CtMethod n2 = CtNewMethod.make(bodyString, ccClass);//直接创建一个方法,带有一个int的参数和返回值

n2.setBody("{System.out.println(\"Point Call to method \");return $1;}");

ccClass.addMethod(n2);


/**

* 这里无法用new的形式来创建一个对象,因为已经classloader中不能有两个相同的对象,否则会报异常如下:

*Caused by: java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader):

*attempted duplicate class definition for name: "Point"

**/

Object oo = ccClass.toClass().newInstance();

Method mms = oo.getClass().getMethod("execute", null);

System.out.println("new class name is : " + oo.getClass().getName());

System.out.println("new class's method is : " + mms.invoke(oo, null));

System.out.println("---------------------------------------------");

//这一行代码将class冻结了,下面无法再对类多编辑或者修改,下面的setName会报异常如:

//Exception in thread "main" java.lang.RuntimeException: Point class is frozen

ccClass.freeze();

try{

ccClass.setName("Point2");

}catch (Exception e) {

System.out.println(e);

}

//对已经冻结的class解冻之后还可以继续编辑修改

ccClass.defrost();

System.out.println("------------- 上面的代码是对的,下面的代码将会无法执行出结果,会报错------------------------");

Class[] params = new Class[1];

Integer num = new Integer(0);

params[0] = num.getClass();

mms = oo.getClass().getMethod("getNumber",params);

System.out.println("new class name is : " + oo.getClass().getName());

System.out.println("new class's method is : " + mms.invoke(oo, 100));

System.out.println("---------------------------------------------");

}

}


















举报

相关推荐

0 条评论