如何使用com.sun.codemodel将生成的Java类转为字符串而非写入文件
获取JCodeModel生成的Java类字符串
嘿,这个需求我之前也碰到过!你猜的没错,扩展com.sun.codemodel.CodeWriter就是解决问题的核心——JCodeModel本身没有直接提供获取生成代码字符串的API,但通过自定义CodeWriter,我们可以把生成的代码捕获到内存中,而不是直接写入文件。
下面是具体的实现步骤:
1. 自定义StringCodeWriter类
我们需要创建一个继承自CodeWriter的子类,用Map存储每个生成类对应的源代码字符串,同时处理编码和资源关闭逻辑:
import com.sun.codemodel.CodeWriter; import com.sun.codemodel.JPackage; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; public class StringCodeWriter extends CodeWriter { // 存储包路径+类名到源代码字符串的映射 private final Map<String, String> sourceMap = new HashMap<>(); @Override public OutputStream openBinary(JPackage pkg, String fileName) throws IOException { // 构建类的唯一标识(包名+文件名) String key = pkg.name().isEmpty() ? fileName : pkg.name() + "." + fileName; // 使用ByteArrayOutputStream捕获输出 return new ByteArrayOutputStream() { @Override public void close() throws IOException { super.close(); // 将字节转换为字符串并存入Map sourceMap.put(key, new String(this.toByteArray(), StandardCharsets.UTF_8)); } }; } @Override public void close() throws IOException { // 无外部资源需要关闭,留空即可 } // 获取指定类的源代码字符串 public String getSource(String className) { return sourceMap.get(className + ".java"); } // 获取所有生成的源代码 public Map<String, String> getAllSources() { return new HashMap<>(sourceMap); } }
2. 使用自定义Writer捕获生成的代码
接下来,用StringCodeWriter代替默认的FileCodeWriter调用JCodeModel的build方法,就能轻松拿到字符串了:
import com.sun.codemodel.JCodeModel; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JMod; public class CodeModelDemo { public static void main(String[] args) throws Exception { JCodeModel codeModel = new JCodeModel(); // 生成一个简单的Java类示例 JDefinedClass demoClass = codeModel._class(JMod.PUBLIC, "com.example.DemoClass"); demoClass.field(JMod.PRIVATE, String.class, "name"); demoClass.method(JMod.PUBLIC, void.class, "setName") .param(String.class, "name") .body().assign(demoClass.ref("name"), demoClass.ref("name")); // 使用自定义StringCodeWriter StringCodeWriter writer = new StringCodeWriter(); codeModel.build(writer); // 获取生成的源代码字符串 String demoSource = writer.getSource("com.example.DemoClass"); System.out.println(demoSource); // 如果要获取所有生成的类,调用getAllSources() writer.getAllSources().forEach((key, source) -> { System.out.println("\n--- " + key + " ---"); System.out.println(source); }); } }
3. 同时写入文件并获取字符串(可选)
如果你需要同时保留写入文件的操作,还可以实现一个CompositeCodeWriter,同时委托给FileCodeWriter和StringCodeWriter:
import com.sun.codemodel.CodeWriter; import com.sun.codemodel.JPackage; import java.io.IOException; import java.io.OutputStream; public class CompositeCodeWriter extends CodeWriter { private final CodeWriter[] writers; public CompositeCodeWriter(CodeWriter... writers) { this.writers = writers; } @Override public OutputStream openBinary(JPackage pkg, String fileName) throws IOException { OutputStream[] streams = new OutputStream[writers.length]; for (int i = 0; i < writers.length; i++) { streams[i] = writers[i].openBinary(pkg, fileName); } // 返回一个同时写入多个流的OutputStream return new OutputStream() { @Override public void write(int b) throws IOException { for (OutputStream stream : streams) { stream.write(b); } } @Override public void close() throws IOException { for (OutputStream stream : streams) { stream.close(); } } @Override public void flush() throws IOException { for (OutputStream stream : streams) { stream.flush(); } } }; } @Override public void close() throws IOException { for (CodeWriter writer : writers) { writer.close(); } } }
使用方式也很简单:
// 创建文件写入器和字符串写入器 FileCodeWriter fileWriter = new FileCodeWriter(new File("./generated")); StringCodeWriter stringWriter = new StringCodeWriter(); // 组合两个写入器 codeModel.build(new CompositeCodeWriter(fileWriter, stringWriter)); // 之后既可以在文件系统看到生成的类,也能通过stringWriter拿到字符串
这个方案完全符合你的需求,既可以替代写入文件的操作,也可以同时保留两种输出方式,而且完全基于JCodeModel的扩展机制,不需要修改框架源码。
内容的提问来源于stack exchange,提问作者Hector
相关产品推荐
相关产品推荐

