利用jvm的指令集直接构造class,简单的bean class 还是有应用场景的。在此利用ASM和Javassist各造例子以备忘!
抽象类:SimpleJbean
public abstract class SimpleJbean { public abstract byte[] createBeanClass(String className, List<FieldInfo> fields); }
ASM实现:
import java.util.List; import org.apache.commons.lang.StringUtils; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import com.FieldInfo; import com.SimpleJbean; /** * SimpleJbean.java. 2011-12-28下午4:12:18 @author LionBule. */ public class SimpleJbeanAsm extends SimpleJbean implements Opcodes { @Override public byte[] createBeanClass(String className, List<FieldInfo> fields) { ClassWriter cw = new ClassWriter(0); cw.visit(V1_1, ACC_PUBLIC, className, null, "java/lang/Object", null); // creates a MethodWriter for the (implicit) constructor MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); mv.visitInsn(RETURN); mv.visitMaxs(1, 1); // create set&get methods for (FieldInfo f : fields) { addMethod(cw, mv, className, f); } return cw.toByteArray(); } private void addMethod(ClassWriter cw, MethodVisitor mv, String className, FieldInfo fieldInfo) { String fieldName = fieldInfo.name; String setMethodName = "set" + StringUtils.capitalize(fieldName); String getMethodName = "get" + StringUtils.capitalize(fieldName); String typeof = Type.getType(fieldInfo.type).getDescriptor(); String getof = getof(typeof); String setof = setof(typeof); int[] loadAndReturnOf = loadAndReturnOf(typeof); //add field cw.visitField(ACC_PRIVATE, fieldName, typeof, null, 0).visitEnd(); // getMethod mv = cw.visitMethod(ACC_PUBLIC, getMethodName, getof, null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, className, fieldName, typeof); mv.visitInsn(loadAndReturnOf[1]); mv.visitMaxs(2, 1); mv.visitEnd(); // setMethod mv = cw.visitMethod(ACC_PUBLIC, setMethodName, setof, null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(loadAndReturnOf[0], 1); mv.visitFieldInsn(PUTFIELD, className, fieldName, typeof); mv.visitInsn(RETURN); mv.visitMaxs(3, 3); mv.visitEnd(); } private String setof(String typeof) { return "(" + typeof + ")V"; } private String getof(String typeof) { return "()" + typeof; } private int[] loadAndReturnOf(String typeof) { if (typeof.equals("I") || typeof.equals("Z")) { return new int[]{ILOAD,IRETURN}; } else if (typeof.equals("J")) { return new int[]{LLOAD,LRETURN}; } else if (typeof.equals("D")) { return new int[]{DLOAD,DRETURN}; } else if (typeof.equals("F")) { return new int[]{FLOAD,FRETURN}; } else { return new int[]{ALOAD,ARETURN}; } } }
Javassist实现:
import java.util.List; import org.apache.commons.lang.StringUtils; import com.FieldInfo; import com.SimpleJbean; import javassist.ClassPool; import javassist.CtClass; import javassist.CtField; import javassist.CtMethod; import javassist.CtNewMethod; /** * JbeanUtil.java. 2011-12-26下午4:05:02 @author LionBule. */ public class SimpleJbeanJs extends SimpleJbean { private final static String SETTER_STR = "set"; private final static String GETTER_STR = "get"; // type/fieldName private final static String fieldTemplate = "private %s %s;"; @Override public byte[] createBeanClass(String className, List<FieldInfo> fields){ try{ ClassPool cpool = ClassPool.getDefault(); CtClass cc = cpool.makeClass(StringUtils.capitalize(className)); String setMethodName = null; String getMethodName = null; for (FieldInfo fi : fields) { setMethodName = SETTER_STR + StringUtils.capitalize(fi.name); getMethodName = GETTER_STR + StringUtils.capitalize(fi.name); CtField newField = CtField.make(String.format(fieldTemplate, fi.type.getName(), fi.name), cc); cc.addField(newField); CtMethod ageSetter = CtNewMethod.setter(setMethodName, newField); cc.addMethod(ageSetter); CtMethod ageGetter = CtNewMethod.getter(getMethodName, newField); cc.addMethod(ageGetter); } return cc.toBytecode(); }catch(Exception e){ throw new RuntimeException(e); } } }
单元测试:
import static org.junit.Assert.*; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang.StringUtils; import org.junit.Before; import org.junit.Test; import com.asm.util.SimpleJbeanAsm; import com.javassist.util.SimpleJbeanJs; /** * SimpleJbeanTest.java. 2011-12-30下午9:00:09 @author LionBule. */ public class SimpleJbeanTest extends ClassLoader { private String className; private List<FieldInfo> fields; @Before public void setUp() throws Exception { className = "User"; FieldInfo testString = new TestFieldInfo(String.class, "name", "lionbule"); FieldInfo testInt = new TestFieldInfo(int.class, "age", 27); FieldInfo testLong = new TestFieldInfo(long.class, "count", 9999999999L); FieldInfo testFloat = new TestFieldInfo(float.class, "score", 89.312F); FieldInfo testDouble = new TestFieldInfo(double.class, "number", 89.3121313D); FieldInfo testBoolean = new TestFieldInfo(Boolean.class, "isStudent", false); fields = new ArrayList<FieldInfo>(); fields.add(testString); fields.add(testInt); fields.add(testLong); fields.add(testFloat); fields.add(testDouble); fields.add(testBoolean); } @Test public void testSimpleJbeanJs() { try { SimpleJbean simpleJbean = new SimpleJbeanJs(); doAction(simpleJbean); } catch (Exception e) { e.printStackTrace(); fail(e.getMessage()); } } @Test public void testSimpleJbeanAsm() { try { SimpleJbean simpleJbean = new SimpleJbeanAsm(); doAction(simpleJbean); } catch (Exception e) { e.printStackTrace(); fail(e.getMessage()); } } private void doAction(SimpleJbean simpleJbean) throws Exception { byte[] classBytes = simpleJbean.createBeanClass(className, fields); Class<?> userClass = this.defineClass(className, classBytes, 0, classBytes.length); //invoker Object user = userClass.newInstance(); TestFieldInfo tempField = null; for (FieldInfo t : fields) { tempField = (TestFieldInfo) t; String setMethodName = "set" + StringUtils.capitalize(tempField.name); String getMethodName = "get" + StringUtils.capitalize(tempField.name); Method setMethod = userClass.getMethod(setMethodName, new Class[] { tempField.type }); setMethod.invoke(user, new Object[] { tempField.value }); Method getMethod = userClass.getMethod(getMethodName, new Class[] {}); System.out.println(getMethod.toGenericString()); Object result = getMethod.invoke(user, new Object[] {}); assertEquals(tempField.value, result); } } }
ASM和Javassist的区别和侧重点,google下有很多资料,不再详述。
看了上面列子也会有大概的感触!
源码地址:
http://lionbule-java-open.googlecode.com/svn/trunk/lionbule-compile
reference:
1. http://asm.ow2.org/doc/developer-guide.html
2. http://download.forge.objectweb.org/asm/asm4-guide.pdf