生成Javaassist运行时POJO时发生异常

时间:2019-06-28 13:07:34

标签: java exception pojo javassist

创建POJO时出现以下错误:

java.lang.RuntimeException: act_ge_bytearray: frozen class (cannot edit)
    at javassist.ClassPool.checkNotFrozen(ClassPool.java:587) ~[javassist-3.25.0-GA.jar:na]
    at javassist.ClassPool.makeClass(ClassPool.java:829) ~[javassist-3.25.0-GA.jar:na]
    at javassist.ClassPool.makeClass(ClassPool.java:806) ~[javassist-3.25.0-GA.jar:na]

我通过以下方式创建POJO:

Class<?> clazz = null;
try {
    clazz = PojoGenerator.generate( "net.javaforge.blog.javassist.Pojo$Generated", props);
} catch (NotFoundException e) {
    e.printStackTrace();
} catch (CannotCompileException e) {
    e.printStackTrace();
}

在选择特定表后将创建对象。初次尝试有效,但其他失败。

例如,在选择对象foo之后的首次尝试中,POJO创建得很好,但是在选择其他项之后出现了问题。

编辑

我已根据以下教程创建了POJO生成器类: https://blog.javaforge.net/post/31913732423/howto-create-java-pojo-at-runtime-with-javassist

编辑2 我正在使用Vaadin表示Web应用程序中PostgreSQL表的架构。

以下代码获取所选表的每一列,并为给定名称创建propertiesgetterssetters。然后我要在所选表中创建尽可能多的记录

package pl.registers.spring.view;

import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import javassist.CannotCompileException;
import javassist.NotFoundException;
import pl.registers.spring.service.DbService;
import pl.registers.spring.test.DatabaseTableModel;
import pojo.PojoGenerator;

import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Route("")
public class TablesView extends VerticalLayout {
    private List<String> tables;

        public TablesView()  {
        Connection con=null;
        try {
            con = DriverManager.getConnection("jdbc:postgresql://localhost:5432/process-engine", "postgres",
                    "postgres");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        ComboBox tablesCombo = new ComboBox();
        try {
            tables = DbService.getInstance().getTableNames(con);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        Map<String, Class<?>> props = new HashMap<String, Class<?>>();
        List items = new ArrayList();
        Grid grid = new Grid();

            try {
                tablesCombo.setItems(DbService.getInstance().getTableNames(con));
            } catch (SQLException e) {
                e.printStackTrace();
            }

            tablesCombo.addValueChangeListener(table -> {
                items.clear();
            try {
                DatabaseTableModel dP = new DatabaseTableModel(DriverManager.getConnection("jdbc:postgresql://localhost:5432/process-engine", "postgres",
                        "postgres"), table.getValue());

                //get column names
                for(int i = 0; i < dP.getColumnCount(); i++) {
                    props.put(dP.getColumnName(i), String.class);
                }

                //get records
                System.out.println("data vector: " + dP.getDataVector());

                //generate Class schema

                Class<?> clazz = null;

                try {
                    clazz = PojoGenerator.generate(
                            "net.javaforge.blog.javassist.Pojo$Generated", props);
//                            table.getValue().toString(), props);
                } catch (NotFoundException e) {
                    e.printStackTrace();
                } catch (CannotCompileException e) {
                    e.printStackTrace();
                }

                //create as many objects as many records
                try {
                    for(int i = 0; i < dP.getDataVector().size(); i++) {
                        items.add(clazz.newInstance());
                    }
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

                List dataVectorCastedToList = dP.getDataVector();

                //set objects values
                for(int i = 0; i <= items.size()-1; i++) {
                    try {
                        clazz.getMethod("Constructor", java.util.List.class).invoke(items.get(i), dataVectorCastedToList.get(i));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }
                }

                List<String> getters = new ArrayList<>();
                //get methods
                for(int i = 0; i < clazz.getMethods().length-1; i++){
                    String methodName = clazz.getMethods()[i].getName();
                    if(methodName.contains("get")) {
                        getters.add(methodName);
                    }
                }

                //remove getClass method
                if(getters.contains("getClass")) 
     getters.remove("getClass");
                System.out.println("getters" + getters);

                grid.setItems(items);

                //set columns
                getters.forEach(item -> {
                    grid.addColumn(column -> {
                         try {
                            return 
 column.getClass().getMethod(item).invoke(column);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        } catch (NoSuchMethodException e) {
                            e.printStackTrace();
                        }
                        return null;
                    }).setHeader(item);
                });

            } catch (SQLException e) {
                e.printStackTrace();
            }
        });

        //add to layout
        add(tablesCombo, grid);
    }
}

我还在generateInitMethod类中创建了自己的PojoGenerator方法,该方法为创建的对象设置数据。 PojoGenerator的样子如下:

package pojo;

import javassist.*;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class PojoGenerator {

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

        System.out.println("properties passed to pojo generator: " + properties);

        ClassPool pool = ClassPool.getDefault();
        cc = pool.makeClass(className);
        System.out.println("is frozen " + cc.isFrozen());

        cc.debugWriteFile();

        // 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 (Map.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()));
        }

        cc.addMethod(generateInitMethod(cc, properties));
        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 CtMethod generateInitMethod(CtClass declaringClass, Map<String, Class<?>> properties)
            throws CannotCompileException {

        String setterName = "Constructor";

        List<String> fields = new ArrayList<>();

        //set attributes names
        properties.forEach((e, v) -> {
            fields.add(e);
        });

        String body = "";

        for (int i = 0; i < fields.size(); i++) {
            body += "this.".concat(fields.get(i)).concat("=").concat("(String)arguments.get(").concat(String.valueOf(i)).concat(")").concat(";");
        }

        StringBuffer sb = new StringBuffer();

        sb.append("public void ").append(setterName).append("(").append("java.util.List arguments").append(")")
                .append("{")
                .append(body)
                .append("}");

        System.out.println(sb);

        return CtMethod.make(sb.toString(), declaringClass);
    }

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

}

很抱歉难看的代码,但只有最低版本。

任何人都可以提供有关如何解决此问题的指导吗?

1 个答案:

答案 0 :(得分:0)

实际上,这是Javassist库所做的检查,因为Java中的类名和包不能相同:

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

上面的将失败,因为第一条语句已经生成了Pojo类。在命名第二个类Pojo2时,以下代码将起作用:

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

我从您的完整示例中看到,这是通过某些外部调用动态生成的,因此,为了解决该问题,您可能会找到一种存储表生成结果(即本地地图)并调用generate的方法。仅在缺少此内容时。就像这样:

Map<String, Class<?>> cache = new HashMap<String, Class<?>>();    
...
Class<?> clazz = null;
try {
    if (cache.containsKey("your.package.yourTableName$Generated")) {
        clazz = cache.get("your.package.yourTableName$Generated");
    } else {
        clazz = PojoGenerator.generate( "your.package.yourTableName$Generated", props);
        cache.put("your.package.yourTableName$Generated", clazz);
    }
}...