在开发过程中,经常会遇到第三方jar包的类提供的服务不能满足我们的需要。这时我们通常的做法是定义一个子类,复写父类方法,用子类实例化对象,其引用类型不变。 这种方式的特点是:
1. 修改源文件,新定义java文件
2. 编译.java文件为.class文件
3. 由classLoader加载字节码文件到内存中,由解析器来执行
现存在这么一种场景,如taobao开放平台,界面上有很多的选项框,我们只要选择相应的接口、方法,输入分配给我们的帐号,然后在文本框中编写一段java调用代码,就可以在页面上看到返回结果。
此时需要在运行的情况下,动态编译字符串代码,在内存中生成新的class文件,后续步骤同上
目前有很多成熟的开源项目支持分析、编辑和创建Java字节码,如cglib、asm、javassist
本文以javassist为例子,通过一个简单例子来描述上面是如何实现。
Person.java
package com.alibaba.model; /** * 类Person.java的实现描述:TODO 类实现描述 * * @author onlyone 2012-6-17 下午04:19:49 */ public class Person { private String name; private String age; private String address; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
AssistFactory.java
package com.alibaba.factory; import java.util.concurrent.atomic.AtomicInteger; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import com.alibaba.model.Person; /** * 类AssistFactory.java的实现描述:TODO 类实现描述 * * @author onlyone 2012-6-17 下午04:20:46 */ public class AssistFactory { // Class载入器 private static ClassPool pool; // 原子计数器 private static AtomicInteger number = new AtomicInteger(1); static { pool = ClassPool.getDefault(); } public void compileAndExe(String body) throws Exception { String name = Person.class.getName(); // 新定义的子类,可以修改 CtClass cc = pool.makeClass(name + "$" + number.incrementAndGet()); // 父类 cc.setSuperclass(pool.get(name)); // 复写父类方法 String method = "public String getName(){ System.out.println(super.getName()+\" is %s!\"); return super.getName(); }"; method=String.format(method, body); // 将新方法添加到类中 cc.addMethod(CtMethod.make(method, cc)); // 类模板 Class<?> c = cc.toClass(); cc.detach(); // 实例化对象 Person p = (Person) c.newInstance(); p.setName("onlyone"); p.getName(); } public static void main(String[] args) throws Exception { new AssistFactory().compileAndExe("sign"); } }
结果:
onlyone is sign!
代码下载地址:https://javassist.googlecode.com/svn/trunk/