能否通过JSON文件在运行时动态创建Java类?
动态生成指定类与方法的实现方案
需求回顾
给定如下JSON定义:
{ "Invoice_iDoc": { "name": "Invoice", "methods": [ "customer", "project", "cost" ] }, "PO": { "name": "PO", "methods": [ "date", "amount" ] } }
需要动态创建Invoice(包含customer(String)、project(String)、cost(String)方法)和PO(包含date(String)、amount(String)方法)类,且无需提前存在类文件。
问题解答
1. 能否创建带有指定方法的类?
可以实现。无需提前编写类文件,通过字节码生成技术或动态代理就能构建出包含指定方法的类。
2. 能否在运行时生成这些类?
完全可行,Java提供了多种运行时生成类的方式,以下是两种常用方案:
方案一:动态代理(基于接口)
如果可以接受基于接口的实现,动态代理是轻量的选择:
- 定义通用接口适配方法调用
- 通过
Proxy.newProxyInstance生成代理类,在InvocationHandler中处理方法逻辑
示例代码:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; public class DynamicProxyDemo { public static void main(String[] args) throws Exception { // 生成Invoice代理实例 Object invoice = createDynamicProxy(new String[]{"customer", "project", "cost"}); // 调用方法 invoice.getClass().getMethod("customer", String.class).invoke(invoice, "C001"); // 生成PO代理实例 Object po = createDynamicProxy(new String[]{"date", "amount"}); po.getClass().getMethod("date", String.class).invoke(po, "2024-05-20"); } private static Object createDynamicProxy(String[] methodNames) { return Proxy.newProxyInstance( DynamicProxyDemo.class.getClassLoader(), new Class<?>[]{DynamicBusiness.class}, (proxy, method, args) -> { if (Arrays.asList(methodNames).contains(method.getName())) { System.out.printf("调用%s方法,参数:%s%n", method.getName(), args[0]); } return null; } ); } // 通用业务接口 public interface DynamicBusiness { void customer(String param); void project(String param); void cost(String param); void date(String param); void amount(String param); } }
方案二:字节码操作库(生成独立类)
如果需要生成独立的类(而非代理),可以用ByteBuddy、ASM或CGLIB这类库直接在运行时生成字节码。
以ByteBuddy为例,代码示例:
import net.bytebuddy.ByteBuddy; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.implementation.FixedValue; import net.bytebuddy.matcher.ElementMatchers; import java.lang.reflect.Method; public class ByteBuddyDynamicClass { public static void main(String[] args) throws Exception { // 生成Invoice类 DynamicType.Unloaded<?> invoiceType = new ByteBuddy() .subclass(Object.class) .name("com.example.Invoice") .defineMethod("customer", void.class, ElementMatchers.PUBLIC) .withParameter(String.class) .intercept(FixedValue.nullValue()) .defineMethod("project", void.class, ElementMatchers.PUBLIC) .withParameter(String.class) .intercept(FixedValue.nullValue()) .defineMethod("cost", void.class, ElementMatchers.PUBLIC) .withParameter(String.class) .intercept(FixedValue.nullValue()) .make(); // 加载并实例化 Class<?> invoiceClazz = invoiceType.load(ByteBuddyDynamicClass.class.getClassLoader()).getLoaded(); Object invoice = invoiceClazz.getDeclaredConstructor().newInstance(); invoiceClazz.getMethod("customer", String.class).invoke(invoice, "C001"); // 生成PO类 DynamicType.Unloaded<?> poType = new ByteBuddy() .subclass(Object.class) .name("com.example.PO") .defineMethod("date", void.class, ElementMatchers.PUBLIC) .withParameter(String.class) .intercept(FixedValue.nullValue()) .defineMethod("amount", void.class, ElementMatchers.PUBLIC) .withParameter(String.class) .intercept(FixedValue.nullValue()) .make(); Class<?> poClazz = poType.load(ByteBuddyDynamicClass.class.getClassLoader()).getLoaded(); Object po = poClazz.getDeclaredConstructor().newInstance(); poClazz.getMethod("date", String.class).invoke(po, "2024-05-20"); } }
Maven依赖:
<dependency> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy</artifactId> <version>1.14.11</version> </dependency>
总结
- 动态代理适合快速实现方法拦截逻辑,无需额外依赖;字节码库适合生成独立的、可持久化的类(可将生成的字节码写入文件保存)。
- 两种方案都支持运行时生成类,无需提前存在类文件。
内容的提问来源于stack exchange,提问作者Java Learner
相关产品推荐
相关产品推荐

