Muh*_*edy 11 java bytecode bcel
我有以下情况..
我正在编写一些工具,对数据库运行用户输入的查询并返回结果.
最简单的方法是将结果返回为:List<String[]>
但我需要更进一步.
我需要创建(在运行时)一些具有某些名称的POJO(或DTO),并为其创建字段和setter以及getter,并使用返回的数据填充它,然后将其返回给用户,并.class
生成文件...
所以这里的想法是如何在运行时创建简单的类(字节码)(动态) 我做一个基本的搜索,发现很多lib 包括Apache BCEL但是我觉得我需要更简单的东西......
你对那个怎么想的?
谢谢.
Sea*_*oyd 17
如果使用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();
}
Run Code Online (Sandbox Code Playgroud)
测试代码:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
class some.ClassName
public int [] some.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)
但问题是:你无法对这些方法进行编码,因为它们在编译时不存在,所以我不知道这会对你有什么好处.
我过去曾为此使用过 ASM。我喜欢的是 ASMifier,它可以创建代码来生成一个类。例如,我在 Java 代码中创建了一个通用 POJO,其中包含 Java 中每种类型的一个字段,并使用 ASMifier 创建 Java 代码以从字节码创建它,并将其用作模板来生成任意 POJO。
正如@Michael 所建议的,您可能想要添加一种非反射方式来获取任意字段。例如
public Set<String> fieldNames();
public Object getField(String name);
public void setField(String name, Object name);
Run Code Online (Sandbox Code Playgroud)
你为什么要这样做?有一些方法可以使使用Map<String, Object>
样式对象比使用常规地图更有效。
另一种方法是使用 Velocity 生成 Java 源代码并使用 Compiler API 编译代码。它使用起来很痛苦,所以我在这里为它编写了一个包装Essence JCF使用这种方法的唯一读取优势是您可以轻松调试生成的代码。(该库可以选择将 java 代码保存到调试器可以找到的地方,以便在您进入生成的代码时)
好吧,也可以尝试一下。但是,如果有人可以解释,我需要理解这一点。
更新:
假设您的应用程序必须在运行时根据某些外部配置动态创建Java POJO实例。使用字节码操作库之一可以轻松完成此任务。这篇文章演示了如何使用Javassist库来完成此工作。
假设我们对动态创建的POJO应该包含的属性进行以下配置:
Map<String, Class<?>> props = new HashMap<String, Class<?>>();
props.put("foo", Integer.class);
props.put("bar", String.class);
Run Code Online (Sandbox Code Playgroud)
让我们编写一个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());
}
}
Run Code Online (Sandbox Code Playgroud)
而已!
使用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);
}
Run Code Online (Sandbox Code Playgroud)
执行以上操作将产生以下控制台输出:
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!
Run Code Online (Sandbox Code Playgroud)
调用者会如何处理动态生成的类,因此他的代码无法知道该类?访问它的唯一方法是通过反射。返回List<String[]>
orMap<String, String>
实际上是一个更干净、更可用的设计。