`

利用ASM和Javassist动态生成Class 类(set和get)

阅读更多

利用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

 

 

 

分享到:
评论
9 楼 diamondy 2015-07-14  
sunny80062951414 写道
没有fieldinfo信息啊。



public class FieldInfo {

public String name; 
public Class<?> type;

public FieldInfo(Class<?> type, String name) {
this.type = type;
this.name = name;

}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Class<?> getType() {
return type;
}

public  void setType(Class<?> type) {
this.type = type;
}

}


public class TestFieldInfo extends FieldInfo {
public Object value;

public TestFieldInfo(Class<?> type, String name, Object value) {
super(type, name);
this.value = value;
}
}
8 楼 sunny80062951414 2015-01-07  
没有fieldinfo信息啊。
7 楼 bjzhangliang 2013-11-20  
TestFieldInfo类:

public class TestFieldInfo extends FieldInfo{
public Object value;
public TestFieldInfo(Class<?> type, String name, Object value) {
super(type, name);
this.value = value;
}
}

如果有不对的地方,请指正.
6 楼 bjzhangliang 2013-11-19  
SimpleJbean 是抽象 的
5 楼 bjzhangliang 2013-11-19  
TestFieldInfo:这个类怎么写?
4 楼 lionbule 2012-02-20  
Mr_caochong 写道
Mr_caochong 写道
非常好的例子,对我帮助很大,可是请问,如果生成package信息呢


这个问题已经解决了。


建议阅读ASM相关的官方资料,有很完整的讲解! javassist是比较傻瓜化的实现,具体选型由自己的需求决定。
3 楼 Mr_caochong 2012-02-08  
Mr_caochong 写道
非常好的例子,对我帮助很大,可是请问,如果生成package信息呢


这个问题已经解决了。
2 楼 Mr_caochong 2012-02-08  
请问extends 怎么写
1 楼 Mr_caochong 2012-02-07  
非常好的例子,对我帮助很大,可是请问,如果生成package信息呢

相关推荐

    ASM操作字节码,动态生成Java类class文件

    ASM操作字节码,动态生成Java类class文件,模拟Spring的AOP实现原理。

    asm动态生成class,并且动态加载执行

    NULL 博文链接:https://name327.iteye.com/blog/1554558

    Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)

    NULL 博文链接:https://bijian1013.iteye.com/blog/2382393

    Javassist 修改class字节码

    Javassist 提供了java类库,...与其他同类工具(asm等)不同的是,Javassist提供了两个层面的API: 1.java代码层 2.字节码层 通过java代码层,开发者即时对字节码不是很熟悉,也可以非常方便快速的完成字节码的修改。

    java6string源码-dynamic-proxy:利用ASM、CGLIB、ByteBuddy、Javassist和JDKDynamicP

    通过使用运行时代码生成技术,您可以在不定义Class的情况下创建一个实现某些特定接口或扩展类的实例。 通过直接启动ProxyCreator实例,您可以获得利用 ASM、Javassist、ByteBuddy、CGLIB 和传统 JDK 动态代理的特定...

    asm 最新版手册

    它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元...

    JavaAgent:Javassist 与 Asm JavaAgent 字节码动态编程项目

    JavaAgent Javassist 与 Asm JavaAgent 字节码动态编程项目

    javassist-3.14.0-GA

    Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶...直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

    asm5.0 源码+demo+doc

    它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元...

    asm4-guide.pdf

    它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元...

    asm4.1 jar+demo+doc

    它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元...

    ASM4中文使用指南.zip

    它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元...

    asm-9.0.jar

    ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class文件...

    Javassistjar包

    avassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 ...直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

    asm操作指南(中文)

    ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class...

    cglib 和asm jar包

    总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是...

    asm5.0安装包

    ASM是一套java字节码生成架构,它可以动态生成二进制格式的stub类或其它代理类, 或者在类被java虚拟机装入内存之前,动态修改类。 现在挺多流行的框架都使用到了asm.所以从aop追溯来到了这。

    javassist反射工具

    Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶...直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

    HEX2ASM单片机 hex文件生成asm文件

    可以将keil、wave生成的hex文件反汇编成汇编语言

    08 - ASM使用ClassWrite生成接口 - 简书1

    08 - ASM使用ClassWrite生成接口 - 简书1

Global site tag (gtag.js) - Google Analytics