在运行时创建简单的POJO类(字节码)(动态)

Muh*_*edy 11 java bytecode bcel

我有以下情况..

我正在编写一些工具,对数据库运行用户输入的查询并返回结果.

最简单的方法是将结果返回为:List<String[]>但我需要更进一步.

我需要创建(在运行时)一些具有某些名称的POJO(或DTO),并为其创建字段和setter以及getter,并使用返回的数据填充它,然后将其返回给用户,并.class生成文件...

所以这里的想法是如何在运行时创建简单的类(字节码)(动态) 我做一个基本的搜索,发现很多lib 包括Apache BCEL但是我觉得我需要更简单的东西......

你对那个怎么想的?

谢谢.

Sea*_*oyd 17

如果使用CGLib,使用getter和setter创建简单的POJO很容易:

public static Class<?> createBeanClass(
    /* fully qualified class name */
    final String className,
    /* bean properties, name -> type */
    final Map<String, Class<?>> properties){

    final BeanGenerator beanGenerator = new BeanGenerator();

    /* use our own hard coded class name instead of a real naming policy */
    beanGenerator.setNamingPolicy(new NamingPolicy(){
        @Override public String getClassName(final String prefix,
            final String source, final Object key, final Predicate names){
            return className;
        }});
    BeanGenerator.addProperties(beanGenerator, properties);
    return (Class<?>) beanGenerator.createClass();
}
Run Code Online (Sandbox Code Playgroud)

测试代码:

public static void main(final String[] args) throws Exception{
    final Map<String, Class<?>> properties =
        new HashMap<String, Class<?>>();
    properties.put("foo", Integer.class);
    properties.put("bar", String.class);
    properties.put("baz", int[].class);

    final Class<?> beanClass =
        createBeanClass("some.ClassName", properties);
    System.out.println(beanClass);
    for(final Method method : beanClass.getDeclaredMethods()){
        System.out.println(method);
    }

}
Run Code Online (Sandbox Code Playgroud)

输出:

class some.ClassName
public int [] some.ClassName.getBaz()
public void some.ClassName.setBaz(int [])
public java.lang.Integer some.ClassName.getFoo()
public void some.ClassName.setFoo(java. lang.Integer)
public java.lang.String some.ClassName.getBar()
public void some.ClassName.setBar(java.lang.String)

但问题是:你无法对这些方法进行编码,因为它们在编译时不存在,所以我不知道这会对你有什么好处.

  • 最后一句话应突出显示! (2认同)

Pet*_*rey 5

我过去曾为此使用过 ASM。我喜欢的是 ASMifier,它可以创建代码来生成一个类。例如,我在 Java 代码中创建了一个通用 POJO,其中包含 Java 中每种类型的一个字段,并使用 ASMifier 创建 Java 代码以从字节码创建它,并将其用作模板来生成任意 POJO。

正如@Michael 所建议的,您可能想要添加一种非反射方式来获取任意字段。例如

public Set<String> fieldNames();
public Object getField(String name);
public void setField(String name, Object name);
Run Code Online (Sandbox Code Playgroud)

你为什么要这样做?有一些方法可以使使用Map<String, Object>样式对象比使用常规地图更有效。

另一种方法是使用 Velocity 生成 Java 源代码并使用 Compiler API 编译代码。它使用起来很痛苦,所以我在这里为它编写了一个包装Essence JCF使用这种方法的唯一读取优势是您可以轻松调试生成的代码。(该库可以选择将 java 代码保存到调试器可以找到的地方,以便在您进入生成的代码时)


yas*_*waj 5

好吧,也可以尝试一下。但是,如果有人可以解释,我需要理解这一点。

更新:

假设您的应用程序必须在运行时根据某些外部配置动态创建Java POJO实例。使用字节码操作库之一可以轻松完成此任务。这篇文章演示了如何使用Javassist库来完成此工作。

假设我们对动态创建的POJO应该包含的属性进行以下配置:

Map<String, Class<?>> props = new HashMap<String, Class<?>>();
props.put("foo", Integer.class);
props.put("bar", String.class);
Run Code Online (Sandbox Code Playgroud)

让我们编写一个PojoGenerator,它为给定的类名和一个映射动态生成一个Class对象,其中包含所需的属性:

import java.io.Serializable;
import java.util.Map;
import java.util.Map.Entry;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.NotFoundException;

public class PojoGenerator {

public static Class generate(String className, Map<String, Class<?>>  properties) throws NotFoundException,
        CannotCompileException {

    ClassPool pool = ClassPool.getDefault();
    CtClass cc = pool.makeClass(className);

    // add this to define a super class to extend
    // cc.setSuperclass(resolveCtClass(MySuperClass.class));

    // add this to define an interface to implement
    cc.addInterface(resolveCtClass(Serializable.class));

    for (Entry<String, Class<?>> entry : properties.entrySet()) {

        cc.addField(new CtField(resolveCtClass(entry.getValue()), entry.getKey(), cc));

        // add getter
        cc.addMethod(generateGetter(cc, entry.getKey(), entry.getValue()));

        // add setter
        cc.addMethod(generateSetter(cc, entry.getKey(), entry.getValue()));
    }

    return cc.toClass();
}

private static CtMethod generateGetter(CtClass declaringClass, String fieldName, Class fieldClass)
        throws CannotCompileException {

    String getterName = "get" + fieldName.substring(0, 1).toUpperCase()
            + fieldName.substring(1);

    StringBuffer sb = new StringBuffer();
    sb.append("public ").append(fieldClass.getName()).append(" ")
            .append(getterName).append("(){").append("return this.")
            .append(fieldName).append(";").append("}");
    return CtMethod.make(sb.toString(), declaringClass);
}

private static CtMethod generateSetter(CtClass declaringClass, String fieldName, Class fieldClass)
        throws CannotCompileException {

    String setterName = "set" + fieldName.substring(0, 1).toUpperCase()
            + fieldName.substring(1);

    StringBuffer sb = new StringBuffer();
    sb.append("public void ").append(setterName).append("(")
            .append(fieldClass.getName()).append(" ").append(fieldName)
            .append(")").append("{").append("this.").append(fieldName)
            .append("=").append(fieldName).append(";").append("}");
    return CtMethod.make(sb.toString(), declaringClass);
}

private static CtClass resolveCtClass(Class clazz) throws NotFoundException {
    ClassPool pool = ClassPool.getDefault();
    return pool.get(clazz.getName());
}
}
Run Code Online (Sandbox Code Playgroud)

而已!

使用PojoGenerator非常简单。让我们生成一些POJO,通过其所有方法的反射输出,设置然后获取一些属性:

public static void main(String[] args) throws Exception {

Map<String, Class<?>> props = new HashMap<String, Class<?>>();
props.put("foo", Integer.class);
props.put("bar", String.class);

Class<?> clazz = PojoGenerator.generate(
        "net.javaforge.blog.javassist.Pojo$Generated", props);

Object obj = clazz.newInstance();

System.out.println("Clazz: " + clazz);
System.out.println("Object: " + obj);
System.out.println("Serializable? " + (obj instanceof Serializable));

for (final Method method : clazz.getDeclaredMethods()) {
    System.out.println(method);
}

// set property "bar"
clazz.getMethod("setBar", String.class).invoke(obj, "Hello World!");

// get property "bar"
String result = (String) clazz.getMethod("getBar").invoke(obj);
System.out.println("Value for bar: " + result);

}
Run Code Online (Sandbox Code Playgroud)

执行以上操作将产生以下控制台输出:

Clazz: class net.javaforge.blog.javassist.Pojo$Generated
Object: net.javaforge.blog.javassist.Pojo$Generated@55571e
Serializable? true
public void     net.javaforge.blog.javassist.Pojo$Generated.setBar(java.lang.String)
public java.lang.String     net.javaforge.blog.javassist.Pojo$Generated.getBar()
public java.lang.Integer     net.javaforge.blog.javassist.Pojo$Generated.getFoo()
public void     net.javaforge.blog.javassist.Pojo$Generated.setFoo(java.lang.Integer)
Value for bar: Hello World!
Run Code Online (Sandbox Code Playgroud)

  • 这个答案是从这里逐字复制的:http://blog.javaforge.net/post/31913732423/howto-create-java-pojo-at-runtime-with-javassist (2认同)

Mic*_*rdt 2

调用者会如何处理动态生成的类,因此他的代码无法知道该类?访问它的唯一方法是通过反射。返回List<String[]>orMap<String, String>实际上是一个更干净、更可用的设计。