[英]Creating classes dynamically with Java
我試圖找到有關此的信息,但空手而歸:
我收集可以使用反射或代理在 Java 中動態創建一個類,但我不知道如何。 我正在實現一個簡單的數據庫框架,在其中使用反射創建 SQL 查詢。 該方法以數據庫字段作為參數獲取對象,並基於該對象創建查詢。 但是如果我也可以動態地創建對象本身會非常有用,這樣我就不需要為每個表創建一個簡單的數據包裝器對象。
動態類只需要簡單的字段( String
, Integer
, Double
),例如
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();
}
}
}
可以生成類(通過cglib 、 asm 、 javassist 、 bcel ),但你不應該那樣做。 為什么?
Object
並使用反射獲取所有字段 - 不是一個好主意 如果您只是想要未定義格式的數據,那么您可以在數組中返回它,例如Object[]
或Map<String, Object>
如果您希望它們命名,並從那里獲取它 - 它將為您省去很多麻煩生成不需要的類,其唯一目的是包含一些將通過反射獲得的數據。
您可以做的是使用預定義的類來保存數據,並將它們作為參數傳遞給查詢方法。 例如:
public <T> T executeQuery(Class<T> expectedResultClass,
String someArg, Object.. otherArgs) {..}
因此,您可以對傳遞的expectedResultClass
使用反射來創建該類型的新對象並用查詢結果填充它。
也就是說,我認為您可以使用現有的東西,例如 ORM 框架(Hibernate、EclipseLink)、spring 的JdbcTemplate
等。
為每個表創建一個數據模型類需要幾分鍾時間,您可以使用 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庫完成了它,具有以下場景:
這是包含完整項目的存儲庫的鏈接: 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.