使用Java动态创建类

时间:2010-02-23 17:45:22

标签: java reflection

我试图找到有关此事的信息,但空手而归:

我认为可以使用反射或代理在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;
}

6 个答案:

答案 0 :(得分:25)

有许多不同的方法可以实现这一点(例如代理,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();
      }
    }
}

答案 1 :(得分:19)

可以生成类(通过cglibasmjavassistbcel),但不应该这样做。为什么呢?

  • 使用该库的代码应该使用类型Object并使用反射获取所有字段 - 这不是一个好主意
  • java是静态类型语言,你想引入动态类型 - 它不是那个地方。

如果您只是希望数据采用未定义的格式,那么您可以将其返回到数组中,如Object[]Map<String, Object>,如果您希望它们命名,并从那里获取它 - 它将为了包含一些将通过反射获得的数据的唯一目的,可以省去不需要的类生成的麻烦。

您可以做的是拥有预定义的类来保存数据,并将它们作为参数传递给查询方法。例如:

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

因此,您可以对传递的expectedResultClass使用反射来创建该类型的新对象,并使用查询结果填充它。

那就是说,我认为你可以使用现有的东西,比如ORM框架(Hibernate,EclipseLink),spring JdbcTemplate等。

答案 2 :(得分:3)

为每个表创建一个数据模型类需要几分钟,您可以使用像Hibernate这样的ORM或通过编写自己的JDBC DAO轻松地映射到数据库。这比深入研究反思要容易得多。

您可以创建一个查询表的数据库结构的实用程序,并为您创建数据模型类和DAO。或者,您可以使用Java创建模型并创建一个实用程序来从中创建数据库模式和DAO(使用反射和Java 5注释来协助)。不要忘记javaFieldNames通常与database_column_names不同。

答案 3 :(得分:0)

这是可能的,但(我相信)您需要ASMBCEL之类的内容。

或者,您可以使用功能更强大的内容(例如Groovy)。

答案 4 :(得分:0)

我也不会尝试这个。一个类是数据和代码,您计划将哪种代码与动态数据相关联?

你可能想要的是一个集合 - 或者可能是Hibernate。

你可以在这个系列中玩很多技巧,让它做你想做的事。您可以使用确保其类型或不为null的数据将它们包装在元对象中,而不是将对象直接放入集合中。您可以将整个集合包装在一个强制类型安全性,完整性和关系的类中。我甚至让我的集合能够使用“Validator”类来验证所分配的数据。

验证,结构和初始条目可以来自数据库,XML或代码。

答案 5 :(得分:0)

我知道反射的性能缺点,但是对于我的小项目,我需要这样做,并且我创建了一个项目库,该库将JSON转换为Java,最后在JVM上下文中转换为.class。

任何需要这种东西的人都可以看看我的open source solution,这需要JDK来编译代码。

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