`

利用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字节码操控和分析框架,它能够用来动态生成类或者增强已有类的功能。ASM可以被用来创建Java代理、实现元编程、甚至深入到Java虚拟机(JVM)层面进行性能优化。在Java开发中,ASM库允许我们直接...

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

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

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

    ASM是一个底层的Java字节码操控和分析框架,它可以用来动态生成类或者增强已有类的功能。ASM提供了一套非常底层的API,可以精确地控制类和方法的生成,但同时也需要编写更复杂的代码。ASM主要用于库开发者,而不是...

    10 - ASM使用ClassWrite生成类 - 简书1

    ASM是一个强大的Java字节码操控和分析框架,它可以直接生成和修改Java类的字节码。...总的来说,ASM是一个强大的工具,它允许开发者在运行时动态生成和修改Java类,从而在很多场景下提供灵活和高效的解决方案。

    Javassist 修改class字节码

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

    cglib动态生成java类

    2. **测试**:在单元测试中,可以通过CGLib生成类的模拟对象(mock object),方便对类的方法进行模拟和测试。 3. **动态数据访问**:例如ORM框架如Hibernate,使用CGLib动态创建数据访问对象(DAO)。 总的来说,...

    Java动态生成代码并编译载入

    另外,有一些库如ASM、Javassist和Byte Buddy,它们提供了更高级的字节码操作能力,可以让我们更方便地生成和修改Java类的字节码。这些库通常用于AOP(面向切面编程)、动态代理或者在运行时优化代码等场景。例如,...

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

    在这个“JavaAgent: Javassist与Asm JavaAgent字节码动态编程项目”中,我们将深入探讨如何利用Javassist和ASM这两个库来实现JavaAgent。 首先,Javassist是一个开源的Java字节码操作框架,它使得开发者可以在运行...

    cglib和asm

    ASM是一个字节码操控和分析框架,它可以用来动态生成类或者增强已有类的功能。ASM提供了一套直接操作Java字节码的底层API,允许开发者在运行时对类进行深度定制。ASM的强大之处在于其灵活性,但同时也要求使用者对...

    动态代理-jdk、cglib、javassist.zip

    CGLIB通过字节码技术生成代理类,它使用ASM库来操作字节码,因此可以为非接口类型创建代理。CGLIB的使用主要包括: 1. 引入CGLIB库。 2. 创建Enhancer对象,它是CGLIB的核心,用于生成代理类。 3. 设置回调机制,即...

    03 - ASM和ClassFile - 简书1

    2. 灵活性强:ASM提供了一套灵活的API,允许开发者直接操作字节码,从而实现高度自定义的类修改和生成。 3. 小巧轻便:ASM库本身体积小,适合嵌入式或资源有限的环境。 4. 面向字节码级别:ASM直接处理字节码,而...

    代码生成工具asm-3.2

    ASM是一个强大的Java字节码操控和分析框架,它可以直接生成和修改Java类文件。ASM-3.2是这个工具的一个版本,主要用于动态程序分析和字节码操纵。这个框架广泛应用于许多领域,如代码混淆、性能监控、动态代理以及...

    Java Classloading Mechanism : ClassLoader & ASM & 动态字节码增强

    ASM允许开发者在运行时动态生成和修改类。通过ASM,我们可以: - 创建全新的类、接口或继承已有类的子类。 - 修改已有的类,添加、删除或修改方法、字段。 - 在方法体中插入代码,实现AOP(面向切面编程)的动态代理...

    asm 最新版手册

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

    根据Map 动态生成一个类 动态打印

    Map作为键值对的数据结构,可以很好地映射到类的属性上,通过这种方式,我们可以自动生成类的实例,从而实现灵活的代码生成和数据绑定。 在Java中,动态生成类的一种常见方法是使用Java的`java.lang.reflect.Proxy`...

    ASM4手册中文版.pdf.zip

    ASM是一个低级别的库,可以直接操作和生成类的字节码,这在创建编译器、代码分析工具以及运行时代码修改等方面非常有用。下面将详细解释ASM4及其在JAVA开发中的应用。 首先,ASM4提供了访问和修改Java类和方法的...

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

    `ClassWriter`是ASM提供的一种用于创建字节码表示的类,它可以自动生成字节码的框架信息,使得我们能方便地构建Java类和接口。 首先,我们需要了解ASM的几个关键概念: 1. `ClassVisitor`: 这是ASM的核心接口,它...

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

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

    Javassistjar包

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

    CGLIB 和 JDK生成动态代理类的区别

    它使用ASM库在内存中构建新的字节码,生成的目标类会继承自原始类。因此,对于没有实现接口的类,CGLIB可以提供动态代理功能。 性能方面,CGLIB通常比JDK动态代理更快,因为它是基于字节码生成的子类,而JDK动态...

Global site tag (gtag.js) - Google Analytics