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:
- A fluent interface to set the pojo's properties.
- A build() method that creates a new pojo instance with the given values.
You can use a generated pojo builder like this:
Contact james = new ContactBuilder()
.withSurname("Bond")
.withFirstname("James")
.withEmail("007@secretservice.org")
.build();
Builders can help you create test data by letting you set only the relevant properties.
For more details on:
- Test data builders, see http://c2.com/cgi/wiki?TestDataBuilder and http://www.natpryce.com/articles/000714.html (by Nat Pryce).
- The builder pattern, see http://en.wikipedia.org/wiki/Builder_pattern.
- Fluent interfaces, see http://www.martinfowler.com/bliki/FluentInterface.html (by Martin Fowler)
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:
- JavaWriter 2.5.0 (see 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:
- withName=<String>
specifies the pattern of the builder's name. An asterisk will be
replaced with the pojos simple name. For example, the result of the pattern
Fluent*Builder
will becomeFluentContactBuilder
if the pojo's name isContact
. The default pattern is*Builder
. - withConstructor=<Visibility>
Specifies the visibility of the builder's constructor.
Default is
Visibility.PUBLIC
. - intoPackage=<String>
specifies the package of the generated builder. An asterisk will be
replaced with the pojos package. For example, the result of the pattern
*.util
will becomecom.example.util
if the pojo's package iscom.example
. The default pattern is*
. - withBaseclass=<Class>
specifies the base class of the generated builder. The default class is
Object.class
. - withBuilderInterface=<Class>
specifies the interface of the generated builder. The interface must declare exactly one type parameter
and a build method with this type as return type.
For an example please see
Address.java
,Builder.java
andAddressBuilder.java
. Default isVoid.class
, which means, that no interface should be implemented. - withBuilderProperties=<boolean>
specifies whether the generated builder should define builder-based with-methods using the
builder interface (see above).
For an example please see
Recipient.java
,Builder.java
andRecipientBuilder.java
. Default isfalse
. - includeProperties=<String[]>
specifies which of the pojo's properties will be included into the generated builder. All properties that match any
property pattern in the specified array will be included. All other non-mandatory properties will be excluded.
Mandatory properties are those which are passed as constructor or factory method arguments. They will never be
excluded, neither explicitly nor implicitly.
For an example please see
InputSourceFactory.java
andInputSourceBuilder.java
. Default is*
. - excludeProperties=<String[]>
specifies which of the pojo's properties will be excluded from the generated builder. All property that match any
property pattern in the specified array will be excluded, except those that are mandatory. Mandatory properties are
those which are passed as constructor or factory method arguments. They will never be excluded, neither explicitly
nor implicitly.
For an example please see
CalendarFactory.java
andGregorianCalendarBuilder.java
. Default is the empty array. - withGenerationGap=<boolean>
specifies whether the generation gap pattern is used. If enabled, this
will generate two classes (instead of one), of which one contains the
ordinary builder code, whereas the other class extends the first one and is an empty template for handwritten code.
Please move it out of the generated-sources folder to prevent it from being overwritten.
For examples please see
Player.java
,PlayerBuilder.java
, andAbstractPlayerBuilder.java
. Default isfalse
. - withCopyMethod=<boolean>
specifies whether a copy method should be generated. Use the copy
method to initialize the builder's values from a given pojo instance.
For an example please see
TextEmail.java
andTextEmailBuilder.java
. Default isfalse
. - withOptionalProperties=<Class>
specifies whether the generated builder should define optional-based setter-methods using the
specified 'Optional' type. Examples are Google Guava's
com.google.common.base.Optional
andjava.util.Optional
introduced with Java 8. Default isVoid.class
, which means, that no optional-based setter-methods are generated. - withSetterNamePattern=<String>
specifies the name pattern of the generated setter-methods. An asterisk will be replaced with
the property's original name. Default is
with*
. - withValidator=<Class>
specifies the validator class that should be used to validate the created pojo. The class must
define a
validate
method having one parameter that is compatible with the pojo's type. If the validation fails, the method must throw some runtime exception (or one of its subclasses). For an example please seeCredentials.java
,CredentialsValidator.java
andCredentialsBuilder.java
. - withFactoryMethod=<String>
specifies the name of a static factory method added to the builder class which creates a builder instance.
An asterisk will be replaced by the pojos simple name.
For an example please see
Task.java
andTaskBuilder.java
. Default is""
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:
- Share common PojoBuilder directives in one place.
- Combine directives for other libraries that support meta-annotations.
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:
- The compile phase auto-detects and activates PojoBuilder.
- Generated sources appear at
${project.build.directory}/generated-sources/annotations
. - To keep generated sources in a different directory, configure the
generatedSourcesDirectory
of themaven-compiler-plugin
. See the sample Maven pom. - For incremental compilation, consider using the Maven Processor plugin instead.
- Eclipse users may install m2e-apt for integrated annotation processing.
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:
- Open your project's properties dialog
- Navigate to "Java Build Path" tree node
- Open the "Libraries" tab
- Add
pojobuilder-4.3.1-annotations.jar
to your project classpath - Navigate to "Java Compiler / Annotation Processing" tree node
- Check "Enable project specific settings"
- Check "Enable annotation processing"
- Check "Enable processing in editor"
- Specify the target directory for the generated code in "Generated source directory"
- Navigate to "Java Compiler / Annotation Processing / Factory Path" tree node
- Check "Enable project specific settings"
- Click "Add JARs..."
- Add
pojobuiler-4.3.1-jar-with-dependencies.jar
- Click "OK"
To compile this project's sources, use Gradle (see build.gradle).