A Java Code Generator for Pojo Builders
Author: Michael Karneim
Project Homepage: http://github.com/mkarneim/pojobuilder
PojoBuilder is a Java annotation processor that creates a fluent builder class for POJOs (Plain Old Java Object).
The generated builder offers:
You can use a generated pojo builder like this:
Contact james = new ContactBuilder()
.withSurname("Bond")
.withFirstname("James")
.withEmail("[email protected]")
.build();
Builders can help you create test data by letting you set only the relevant properties.
For more details on:
The source code in the “src” directory is in the PUBLIC DOMAIN.
For details, read the COPYING file.
PojoBuilder is a code generator. It does not add runtime dependencies to your project.
It adds one compile-time dependency with its own license:
PojoBuilder is open source. The source code is available at
http://github.com/mkarneim/pojobuilder.
For older versions and a change log, see the release history page.
PojoBuilder binaries are available at Sonatype OSS Maven Repository and Maven Central.
If you do not use a build automation tool that supports Maven repos, download the pojobuilder-4.3.1-jar-with-dependencies.jar
to get PojoBuilder with all its dependencies.
PojoBuilder uses an annotation processor to generate pojo builders.
You can trigger code generation during compilation by:
To generate a builder, annotate one of the pojo’s constructors with @GeneratePojoBuilder
.
Example:
public class Contact {
private final String surname;
private final String firstname;
private String email;
@GeneratePojoBuilder
public Contact(String surname, String firstname) {
this.surname = surname;
this.firstname = firstname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getSurname() {
return surname;
}
public String getFirstname() {
return firstname;
}
}
The @GeneratePojoBuilder annotation tells the processor to create a source file named ContactBuilder
. Check ContactBuilder.java
to see the generated code.
Ensure the constructor is public or accessible to the generated builder (for example, if it is protected, the builder must be in the same package).
Also, the constructor parameter names must match the pojo’s property names exactly.
You can use an optional @ConstructorProperties annotation to map constructor parameter names to bean property names if they differ.
public class Contact {
private final String surname;
private final String firstname;
private String email;
@GeneratePojoBuilder
@ConstructorProperties({"surname","firstname"})
public Contact(String arg1, String arg2) {
this.surname = arg1;
this.firstname = arg2;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getSurname() {
return surname;
}
public String getFirstname() {
return firstname;
}
}
If your pojo does not define a specific constructor, has only a public default constructor, or defines exactly one constructor, annotate the class with @GeneratePojoBuilder…
Example:
@GeneratePojoBuilder
public class User {
private String name;
private char[] password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char[] getPassword() {
return password;
}
public void setPassword(char[] password) {
this.password = password;
}
}
See UserBuilder.java
to review the generated code.
If you cannot modify the pojo’s source or prefer not to annotate the pojo, annotate a factory method:
public class UrlFactory {
@GeneratePojoBuilder(withName="UrlBuilder", intoPackage = "samples")
public static URL createUrl(
String protocol, String host, int port, String file, URLStreamHandler handler)
throws MalformedURLException {
return new URL(protocol, host, port, file, handler);
}
}
Check UrlBuilder.java
to see the generated code.
Ensure the factory method is public and static. Its parameter names must match the pojo’s property names exactly.
You can use an optional @FactoryProperties annotation to map the parameter names if needed.
public class FileFactory {
@GeneratePojoBuilder(intoPackage = "samples")
@FactoryProperties({"path"})
public static File createFile(String arg1) {
return new File(arg1);
}
}
See FileBuilder.java
for the generated code.
Starting with PojoBuilder 4.3, you can annotate a Java 17 record:
@GeneratePojoBuilder
public record MyRecord(int x, int y, String blah) {}
You can use the following elements of @GeneratePojoBuilder to configure code generation:
Fluent*Builder
will becomeFluentContactBuilder
if the pojo’s name is Contact
. The default pattern is *Builder
.Visibility.PUBLIC
.*.util
will becomecom.example.util
if the pojo’s package is com.example
. The default pattern is *
.Object.class
.Address.java
, Builder.java
and AddressBuilder.java
.Void.class
, which means, that no interface should be implemented.Recipient.java
, Builder.java
and RecipientBuilder.java
.false
.InputSourceFactory.java
and InputSourceBuilder.java
.*
.CalendarFactory.java
and GregorianCalendarBuilder.java
.Player.java
, PlayerBuilder.java
, and AbstractPlayerBuilder.java
.false
.TextEmail.java
and TextEmailBuilder.java
.false
.com.google.common.base.Optional
andjava.util.Optional
introduced with Java 8. Default is Void.class
, which means,with*
.validate
method having one parameter that is compatible with the pojo’sCredentials.java
, CredentialsValidator.java
and CredentialsBuilder.java
.Task.java
and TaskBuilder.java
.""
meaning not to generate this method.Starting with version 3, PojoBuilder supports meta-annotations. You can place @GeneratePojoBuilder on another annotation, and it will be inherited.
Benefits:
Example:
@GeneratePojoBuilder(withName = "Fluent*Builder")
@lombok.experimental.Value // class-level annotation from Lombok
@javax.annotation.concurrent.Immutable // class-level annotation from JSR-305
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
public @interface AppPojo {
}
Apply it to your pojos:
@AppPojo
public class Contact {
public String name;
}
PojoBuilder generates FluentContactBuilder
based on the directives from @AppPojo
.
You can override meta-annotation defaults with a local @GeneratePojoBuilder
:
@AppPojo
@GeneratePojoBuilder(intoPackage = "builder")
public class Contact {
public String name;
}
This generates FluentContactBuilder
in the package builder
.
The PojoBuilder wiki offers a cookbook on using PojoBuilder to create a domain-specific language for tests.
Find complete code examples in the src/testdata/java/samples folder.
To run the PojoBuilder annotation processor, include it in your compile-time classpath.
At runtime, no libraries are needed because PojoBuilder’s annotations have a retention policy of CLASS
.
Below are brief instructions for running PojoBuilder with:
The javac
compiler detects PojoBuilder if pojobuilder-*.jar
is in the classpath.
Example:
javac -cp pojobuilder-4.3.1-jar-with-dependencies.jar Contact.java
This generates ContactBuilder
if Contact
is annotated with @GeneratePojoBuilder
.
See the javac documentation for more information.
Add the following to your project’s pom.xml
:
<dependency>
<groupId>net.karneim</groupId>
<artifactId>pojobuilder</artifactId>
<version>4.3.1</version>
<!-- 'provided' scope because this is only needed during compilation -->
<scope>provided</scope>
</dependency>
Notes:
${project.build.directory}/generated-sources/annotations
.generatedSourcesDirectory
of the maven-compiler-plugin
. See the sample Maven pom.This script shows how to run the PojoBuilder annotation processor with Gradle.
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
annotationProcessor 'net.karneim:pojobuilder:4.3.1'
compileOnly 'net.karneim:pojobuilder:4.3.1:annotations'
}
Generated sources go to the standard build/classes
directory.
To change the destination:
tasks.compileJava {
options.compilerArgs += ['-s', "$projectDir/src/generated/java"]
}
There is another Gradle script that enables PojoBuilder for Eclipse IDE.
To run PojoBuilder in Eclipse:
pojobuilder-4.3.1-annotations.jar
to your project classpathpojobuiler-4.3.1-jar-with-dependencies.jar
To compile this project’s sources, use Gradle (see build.gradle).