簡體   English   中英

使用 Java 動態創建類

[英]Creating classes dynamically with Java

我試圖找到有關此的信息,但空手而歸:

我收集可以使用反射或代理在 Java 中動態創建一個類,但我不知道如何。 我正在實現一個簡單的數據庫框架,在其中使用反射創建 SQL 查詢。 該方法以數據庫字段作為參數獲取對象,並基於該對象創建查詢。 但是如果我也可以動態地創建對象本身會非常有用,這樣我就不需要為每個表創建一個簡單的數據包裝器對象。

動態類只需要簡單的字段( StringIntegerDouble ),例如

public class Data {
  public Integer id;
  public String name;
}

這可能嗎?我該怎么做?

編輯:這就是我將如何使用它:

/** Creates an SQL query for updating a row's values in the database.
 *
 * @param entity Table name.
 * @param toUpdate Fields and values to update. All of the fields will be
 * updated, so each field must have a meaningful value!
 * @param idFields Fields used to identify the row(s).
 * @param ids Id values for id fields. Values must be in the same order as
 * the fields.
 * @return
 */
@Override
public String updateItem(String entity, Object toUpdate, String[] idFields,
        String[] ids) {
    StringBuilder sb = new StringBuilder();

    sb.append("UPDATE ");
    sb.append(entity);
    sb.append("SET ");

    for (Field f: toUpdate.getClass().getDeclaredFields()) {
        String fieldName = f.getName();
        String value = new String();
        sb.append(fieldName);
        sb.append("=");
        sb.append(formatValue(f));
        sb.append(",");
    }

    /* Remove last comma */
    sb.deleteCharAt(sb.toString().length()-1);

    /* Add where clause */
    sb.append(createWhereClause(idFields, ids));

    return sb.toString();
}
 /** Formats a value for an sql query.
 *
 * This function assumes that the field type is equivalent to the field
 * in the database. In practice this means that this field support two
 * types of fields: string (varchar) and numeric.
 *
 * A string type field will be escaped with single parenthesis (') because
 * SQL databases expect that. Numbers are returned as-is.
 *
 * If the field is null, a string containing "NULL" is returned instead.
 * 
 * @param f The field where the value is.
 * @return Formatted value.
 */
String formatValue(Field f) {
    String retval = null;
    String type = f.getClass().getName();
    if (type.equals("String")) {
        try {
            String value = (String)f.get(f);
            if (value != null) {
                retval = "'" + value + "'";
            } else {
                retval = "NULL";
            }
        } catch (Exception e) {
            System.err.println("No such field: " + e.getMessage());
        }
    } else if (type.equals("Integer")) {
        try {
            Integer value = (Integer)f.get(f);
            if (value != null) {
                retval = String.valueOf(value);
            } else {
                retval = "NULL";
            }
        } catch (Exception e) {
            System.err.println("No such field: " + e.getMessage());
        }
    } else {
        try {
            String value = (String) f.get(f);
            if (value != null) {
                retval = value;
            } else {
                retval = "NULL";
            }
        } catch (Exception e) {
            System.err.println("No such field: " + e.getMessage());
        }
    }
    return retval;
}

有許多不同的方法可以實現這一點(例如代理、ASM),但最簡單的方法,您可以在原型設計時開始:

import java.io.*;
import java.util.*;
import java.lang.reflect.*;

public class MakeTodayClass {
  Date today = new Date();
  String todayMillis = Long.toString(today.getTime());
  String todayClass = "z_" + todayMillis;
  String todaySource = todayClass + ".java";

  public static void main (String args[]){
    MakeTodayClass mtc = new MakeTodayClass();
    mtc.createIt();
    if (mtc.compileIt()) {
       System.out.println("Running " + mtc.todayClass + ":\n\n");
       mtc.runIt();
       }
    else
       System.out.println(mtc.todaySource + " is bad.");
    }

  public void createIt() {
    try {
      FileWriter aWriter = new FileWriter(todaySource, true);
      aWriter.write("public class "+ todayClass + "{");
      aWriter.write(" public void doit() {");
      aWriter.write(" System.out.println(\""+todayMillis+"\");");
      aWriter.write(" }}\n");
      aWriter.flush();      
      aWriter.close();
      }
    catch(Exception e){
      e.printStackTrace();
      }
    }

  public boolean compileIt() {
    String [] source = { new String(todaySource)};
    ByteArrayOutputStream baos= new ByteArrayOutputStream();

    new sun.tools.javac.Main(baos,source[0]).compile(source);
    // if using JDK >= 1.3 then use
    //   public static int com.sun.tools.javac.Main.compile(source);    
    return (baos.toString().indexOf("error")==-1);
    }

  public void runIt() {
    try {
      Class params[] = {};
      Object paramsObj[] = {};
      Class thisClass = Class.forName(todayClass);
      Object iClass = thisClass.newInstance();
      Method thisMethod = thisClass.getDeclaredMethod("doit", params);
      thisMethod.invoke(iClass, paramsObj);
      }
    catch (Exception e) {
      e.printStackTrace();
      }
    }
}

可以生成類(通過cglibasmjavassistbcel ),但你不應該那樣做。 為什么?

  • 使用庫的代碼應該期望類型為Object並使用反射獲取所有字段 - 不是一個好主意
  • java 是靜態類型語言,你想引入動態類型——這不是地方。

如果您只是想要未定義格式的數據,那么您可以在數組中返回它,例如Object[]Map<String, Object>如果您希望它們命名,並從那里獲取它 - 它將為您省去很多麻煩生成不需要的類,其唯一目的是包含一些將通過反射獲得的數據。

您可以做的是使用預定義的類來保存數據,並將它們作為參數傳遞給查詢方法。 例如:

 public <T> T executeQuery(Class<T> expectedResultClass, 
      String someArg, Object.. otherArgs) {..}

因此,您可以對傳遞的expectedResultClass使用反射來創建該類型的新對象並用查詢結果填充它。

也就是說,我認為您可以使用現有的東西,例如 ORM 框架(Hibernate、EclipseLink)、spring 的JdbcTemplate等。

這是可能的,但是(我相信)您需要像ASMBCEL這樣的東西。

或者,您可以使用更強大的東西(例如Groovy )。

為每個表創建一個數據模型類需要幾分鍾時間,您可以使用 Hibernate 等 ORM 或通過編寫自己的 JDBC DAO 輕松地將其映射到數據庫。 這比深入研究反思要容易得多。

您可以創建一個實用程序來查詢表的數據庫結構,並為您創建數據模型類和 DAO。 或者,您可以在 Java 中創建模型並創建一個實用程序來從中創建數據庫模式和 DAO(使用反射和 Java 5 注釋來輔助)。 不要忘記 javaFieldNames 通常不同於 database_column_names。

我知道反射的性能缺陷,但對於我的小項目,我需要這個,我創建了一個項目庫,它將 JSON 轉換為 Java,然后最終在 JVM 上下文中轉換為 .class。

有需要的可以看看我的開源解決方案,需要JDK來編譯代碼。

https://medium.com/@davutgrbz/the-need-history-c91c9d38ec9?sk=f076487e78a1ff5a66ef8eb1aa88f930

最近我需要從元數據(填充靜態數據的對象)創建大約 200 個簡單的類,我通過開源的Burningwave庫完成了它,具有以下場景:

  • 類名稱中需要有特定前綴,例如“Registro”*.java
  • 從超類Registro.java擴展所需的類
  • 需要包含 JPA 注釋的類,如 @ Entity@Column (在屬性中)、 Lombok注釋和自定義注釋。

這是包含完整項目的存儲庫的鏈接: https ://github.com/leandrosoares6/criacao-classes-entidade-efd

下面是負責創建類的代碼片段:

public class RegistrosClassFactory {
    private static final String PACOTE = "com.example.demo.model.registros";
    private static final String SCHEMA = "MY_SCHEMA";
    private static final String PREFIXO = "Registro";

    static void criaRegistros() {
        List<RegistroTest> registros = RegistroMetadataFactory.criaMetadados();
        criaClasses(registros);
    }

    private static void criaClasses(List<RegistroTest> registros) {
        for (RegistroTest registroTest : registros) {
            UnitSourceGenerator gerador = UnitSourceGenerator.create(PACOTE);
            ClassSourceGenerator registro = ClassSourceGenerator
                    .create(TypeDeclarationSourceGenerator.create(PREFIXO + registroTest.getNome()))
                    .addModifier(Modifier.PUBLIC)
                    .addAnnotation(AnnotationSourceGenerator.create(Getter.class))
                    .addAnnotation(AnnotationSourceGenerator.create(Setter.class))
                    .addAnnotation(AnnotationSourceGenerator.create(NoArgsConstructor.class))
                    .addAnnotation(AnnotationSourceGenerator.create(ToString.class))
                    .addAnnotation(AnnotationSourceGenerator.create(Entity.class))
                    .addAnnotation(AnnotationSourceGenerator.create(Table.class)
                            .addParameter("name",
                                    VariableSourceGenerator.create(String.format("\"%s\"",
                                            registroTest.getNomeTabelaBd())))
                            .addParameter("schema", VariableSourceGenerator
                                    .create(String.format("\"%s\"", SCHEMA))));

            criaColunas(registroTest.getCampos(), registro);

            registro.addConstructor(FunctionSourceGenerator.create().addModifier(Modifier.PUBLIC)
                    .addParameter(VariableSourceGenerator.create(String.class, "linha"))
                    .addBodyCodeLine("super(linha);")).expands(Registro.class);
            gerador.addClass(registro);

            // System.out.println("\nRegistro gerado:\n" + gerador.make());
            String caminhoPastaRegistros = System.getProperty("user.dir") + "/src/main/java/";
            gerador.storeToClassPath(caminhoPastaRegistros);
        }
    }

    private static void criaColunas(List<Campo> campos, ClassSourceGenerator registro) {
        for (Campo campo : campos) {
            VariableSourceGenerator field = VariableSourceGenerator
                    .create(TypeDeclarationSourceGenerator.create(String.class),
                            campo.getNomeAtributo())
                    .addModifier(Modifier.PRIVATE)
                    .addAnnotation(AnnotationSourceGenerator.create(Column.class)
                            .addParameter("name", VariableSourceGenerator
                                    .create(String.format("\"%s\"", campo.getNome())))

                    )
                    .addAnnotation(AnnotationSourceGenerator.create(Indice.class).addParameter(
                            "valor",
                            VariableSourceGenerator.create(String.valueOf(campo.getSequencial()))));

            if (campo.getNome().equals("ID")) {
                field.addAnnotation(AnnotationSourceGenerator.create(Id.class));
            }
            if (campo.getEId()) {
                field.addAnnotation(AnnotationSourceGenerator.create(CampoTipoId.class));
            }
            if (campo.getEData()) {
                field.addAnnotation(AnnotationSourceGenerator.create(CampoTipoData.class));
            }
            if (campo.getEDataPart()) {
                field.addAnnotation(AnnotationSourceGenerator.create(CampoTipoDataPart.class));
            }

            registro.addField(field);
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM