@dawn
2016-03-31T08:00:18.000000Z
字数 7109
阅读 314
未分类
很多的时候,我们需要根据注解、配置文件或者其它的方式来运行不同的代码,大多数时候我们可能会使用条件判断(if..else,swith..case),或者java中的反射来实现。
这样方式有一个很大的问题是我们需要在设计之初就考虑到所有的情况,并对外提供统一的接口,第三方实现这些接口即可。
但是这并不是一个一劳永逸的方法,在设计一个框架时,我们考虑的指标往往是非常多的,
还有很多很多
像spring的注解,使用了javassist与cglib,fastJson的序列化与反序列化,为了达到更快的速度,同样使用了javassist
Javassist(Java Programming Assistant)是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。
关于java字节码的处理,目前有很多工具,如bcel,asm。不过这些都需要直接跟虚拟机指令打交道。如果你不想了解虚拟机指令,可以采用javassist。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。
常用的一些类

例如CtClass、CtMember、CtMethod、CtField,可以简单的理解为这是对相应的类、成员封装,提供了一系列基于源码级别的API。
基于javassist开发,不需要了解字节码的一些知识,而且其封装的一些工具类可以简单实现一些高级功能。
参考:
javassist 学习笔记
性能对比
原理
Java编译器编译好Java文件之后,产生.class 文件在磁盘中。这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码。JVM虚拟机读取字节码文件,取出二进制数据,加载到内存中,解析.class 文件内的信息,生成对应的 Class对象。
Javassist的使用很简单,首先获取到class定义的容器ClassPool,通过它获取已经编译好的类(Compile time class),并给这个类设置一个父类,而writeFile讲这个类的定义从新写到磁盘,以便后面使用。
简单的例子
public class E1 {public static void main(String[] args) throws IOException, CannotCompileException, NotFoundException, IllegalAccessException, InstantiationException {ClassPool pool = ClassPool.getDefault();CtClass cc = pool.get("com.yodo1.example.T1");CtMethod cm = cc.getDeclaredMethod("print");cm.insertBefore("{System.out.println(\"call T1.print\");}");//cc.writeFile();T1 t1 = (T1) cc.toClass().newInstance();t1.print();T1 t2 = new T1();t2.print();}}class T1{void print(){System.out.println("t1 print");}}这个例子修改了T1的print方法,最终的输出如下call T1.printt1 printcall T1.printt1 print
一个更加复杂的例子
public class E2 {public static void main(String[] args) throws ClassNotFoundException, CannotCompileException, InstantiationException, NotFoundException, IllegalAccessException {Handler h = getHandler();h.handle("login");h.handle("exit");h.handle("reg");h.handle("doSomething");}public static Handler getHandler() throws NotFoundException, ClassNotFoundException, CannotCompileException, IllegalAccessException, InstantiationException {String s1 = "this is s1";ClassPool pool = ClassPool.getDefault();CtClass ccBean1 = pool.get("com.yodo1.example.Bean1");CtClass ccHandler = pool.get("com.yodo1.example.Handler");ccBean1.addInterface(ccHandler);StringBuilder invokerStr = new StringBuilder("public void handle(String optString) {\n");CtMethod[] ctms = ccBean1.getDeclaredMethods();for (CtMethod cm : ctms) {if (cm.getAnnotation(Opt.class) != null) {String methodName = cm.getName();invokerStr.append("if(optString.equals(\"");invokerStr.append(methodName);invokerStr.append("\")){");invokerStr.append(methodName);invokerStr.append("(); return;}\n");}}invokerStr.append("System.out.println(\"method: \"+ optString + \"() not exist\");\n}");System.out.println("code desc...");System.out.println(invokerStr);System.out.println();System.out.println();CtMethod invokeMethod = CtMethod.make(invokerStr.toString(), ccBean1);invokeMethod.setModifiers(Modifier.PUBLIC);ccBean1.addMethod(invokeMethod);return (Handler) ccBean1.toClass().newInstance();}}//---------@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documented@interface Opt {}//---------interface Handler {void handle(String optString);}//---------class Bean1 {@Optpublic void login() {System.out.println("this is login");}@Optpublic void reg() {System.out.println("this is reg");}@Optpublic void exit() {System.out.println("this is exit");}}
最终的输出如下
code desc...public void handle(String optString) {if(optString.equals("login")){login(); return;}if(optString.equals("reg")){reg(); return;}if(optString.equals("exit")){exit(); return;}System.out.println("method: "+ optString + "() not exist");}this is loginthis is exitthis is regmethod: doSomething() not exist
关于泛型,在jvm中是没有泛型概念的,所有的泛型对象最后都会被转成普通对象,所有,javassit是不支持泛型的。
java字节码操作库:BCEL、cglib、javassit、ObjectWeb ASM、SERP、Package gnu.bytecode 、Cojen、Trove Class File API、Jiapi 、Classfile Reader & Writer 、JBET 、Retroweaver、Jen、Soot、Jdec、JReloader
我们常见的有BCEL、cglib,有的库是直接去操作字节码,在实际的使用中难度稍大,而BCEL、cglib的封装程度与javassit差不多,网上找的两个例子
ObjectWeb ASM
import java.io.FileOutputStream;import java.io.PrintStream;import org.objectweb.asm.ClassWriter;import org.objectweb.asm.MethodVisitor;import org.objectweb.asm.Opcodes;import org.objectweb.asm.Type;import org.objectweb.asm.commons.GeneratorAdapter;import org.objectweb.asm.commons.Method;public class Helloworld extends ClassLoader implements Opcodes {public static void main( final String args[]) throws Exception {// creates a ClassWriter for the Example public class,// which inherits from ObjectClassWriter cw = new ClassWriter( 0 );cw.visit(V1_1, ACC_PUBLIC, "Example" , null , "java/lang/Object" , null );MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>" , "()V" , null ,null );mw.visitVarInsn(ALOAD, 0 );mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object" , "<init>" , "()V" );mw.visitInsn(RETURN);mw.visitMaxs(1 , 1 );mw.visitEnd();mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main" ,"([Ljava/lang/String;)V" , null , null );mw.visitFieldInsn(GETSTATIC, "java/lang/System" , "out" ,"Ljava/io/PrintStream;" );mw.visitLdcInsn("Hello world!" );mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream" , "println" ,"(Ljava/lang/String;)V" );mw.visitInsn(RETURN);mw.visitMaxs(2 , 2 );mw.visitEnd();byte [] code = cw.toByteArray();FileOutputStream fos = new FileOutputStream( "Example.class" );fos.write(code);fos.close();Helloworld loader = new Helloworld();Class exampleClass = loader.defineClass("Example" , code, 0 , code.length);exampleClass.getMethods()[0 ].invoke( null , new Object[] { null });// ------------------------------------------------------------------------// Same example with a GeneratorAdapter (more convenient but slower)// ------------------------------------------------------------------------cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);cw.visit(V1_1, ACC_PUBLIC, "Example" , null , "java/lang/Object" , null );Method m = Method.getMethod("void <init> ()" );GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null , null ,cw);mg.loadThis();mg.invokeConstructor(Type.getType(Object.class ), m);mg.returnValue();mg.endMethod();m = Method.getMethod("void main (String[])" );mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null , null , cw);mg.getStatic(Type.getType(System.class ), "out" , Type.getType(PrintStream.class ));mg.push("Hello world!" );mg.invokeVirtual(Type.getType(PrintStream.class ), Method.getMethod("void println (String)" ));mg.returnValue();mg.endMethod();cw.visitEnd();code = cw.toByteArray();loader = new Helloworld();exampleClass = loader.defineClass("Example" , code, 0 , code.length);exampleClass.getMethods()[0 ].invoke( null , new Object[] { null });}}
cglib
public class MyClass {public void print() {System.out.println("I'm in MyClass.print!" );}}import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;public class Main {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(MyClass.class );enhancer.setCallback(new MethodInterceptorImpl());MyClass my = (MyClass) enhancer.create();my.print();}private static class MethodInterceptorImpl implements MethodInterceptor {public Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable {// log somethingSystem.out.println(method + " intercepted!" );proxy.invokeSuper(obj, args);return null ;}}}最后输出为public void MyClass.print() intercepted!I'm in MyClass.print!