Two months after the first commit in October 2022, Peter Verhas, Senior Architect at EPAM Systems, said: release SourceBuddy, version 2.0.0, is a new utility that compiles dynamically created Java source code. String
or file class
File. SourceBuddy, which requires Java 17, javac
A compiler that provides the same functionality.
Version 2.0.0 supports a combination of hidden and hidden classes at compile time and run time. Additionally, the API has been simplified and includes breaking changes such as: loadHidden()
to the method hidden()
method, hence the new major release. A full summary of changes per version is available in the release documentation on GitHub.
SourceBuddy is available after adding the following Maven dependencies:
<dependency>
<groupId>com.javax0.sourcebuddy</groupId>
<artifactId>SourceBuddy</artifactId>
<version>2.0.0</version>
</dependency>
Alternatively, you can use the following Gradle dependencies:
implementation 'com.javax0.sourcebuddy:SourceBuddy:2.0.0'
To demonstrate SourceBuddy, consider the following example interface used in dynamically created code.
package com.app;
public interface PrintInterface {
void print();
}
A simple API can be compiled one class at a time using static. com.javax0.sourcebuddy.Compiler.compile()
Method.For example, to compile a new class that implements the aforementioned PrintInterface
:
String source = """
package com.app;
public class CustomClass implements PrintInterface {
@Override
public void print() {
System.out.println("Hello world!");
}
}""";
Class<?> clazz = Compiler.compile(source);
PrintInterface customClass =
(PrintInterface) clazz.getConstructor().newInstance();
customClass.print();
The fluent API offers the ability to solve more complex problems such as compiling multiple files. Compiler.java()
static method:
Compiler.java().from(source).compile().load().newInstance(PrintInterface.class);
You can optionally specify the binary name of the class, but SourceBuddy will already detect the name if possible.
.from("com.app", source)
For multiple source files, from()
You can call the method multiple times or load all source files in a particular directory at once.
.from(Paths.get("src/main/java/sourcefiles"))
optionally, hidden()
Methods can be used to create hidden classes that are not directly available to other classes, only through reflection using the class object returned by SourceBuddy.
of compile()
The method generates the byte code of the Java source file, but does not yet load it into memory.
final var byteCodes = Compiler.java()
.from("com.app", source)
.compile();
You can also save the bytecode to your local drive if you prefer.
byteCodes.saveTo(Paths.get("./target/generated_classes"));
or, stream()
You can get information such as the binary name using methods that return a stream of byte arrays.
byteCodes.stream().forEach(
bytecode -> System.out.println(Compiler.getBinaryName(bytecode)));
of byteCodes.load()
The method loads the class and writes the bytecode to Class
object:
final var loadedClasses = compiled.load();
To access the class, cast to the superclass or interface that the class implements, or use the reflection API. for example, CustomClass
:
Class<?> customClass = loadedClasses.get("com.app.CustomClass");
or, newInstance()
You can use methods to create instances of classes.
Object customClassInstance = loadedClasses.newInstance("com.app.CustomClass");
You can use class streams to get detailed information about classes.
loadedClasses.stream().forEach(
clazz -> System.out.println(clazz.getSimpleName()));
For more information on SourceBuddy, README
Files on GitHub.