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

2022-09-03 04:22:12

我有以下场景。

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

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

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

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

对此,你怎么看?

谢谢。


答案 1

如果您使用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();
}

测试代码:

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);
    }

}

输出:

类一些。类名
public int[] 一些。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

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

更新:

想象一下,您的应用程序必须在运行时从某些外部配置动态创建 Java POJO 实例。可以使用其中一个字节码操作库轻松完成此任务。这篇文章演示了如何使用Javassist库来完成此操作。

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

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

让我们编写一个 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());
}
}

就是这样!

使用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);

}

执行上述操作将导致以下控制台输出:

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!

推荐