JavaPoet is a Java API for generating .java source files programmatically with support for modern Java features including records and sealed types
AnnotationSpec is the specification for annotation instances in generated code. It provides a fluent API for building annotations with members and supports both simple and complex annotation values.
Create AnnotationSpec instances from runtime annotations or by building them.
/**
* Creates an AnnotationSpec from a runtime annotation
* @param annotation - The runtime annotation instance
* @return AnnotationSpec representing the annotation
*/
static AnnotationSpec get(Annotation annotation);
/**
* Creates an AnnotationSpec from annotation with option to include defaults
* @param annotation - The runtime annotation instance
* @param includeDefaultValues - Whether to include default member values
* @return AnnotationSpec representing the annotation
*/
static AnnotationSpec get(Annotation annotation, boolean includeDefaultValues);
/**
* Creates an AnnotationSpec from an AnnotationMirror
* @param annotation - The annotation mirror from annotation processing
* @return AnnotationSpec representing the annotation
*/
static AnnotationSpec get(AnnotationMirror annotation);
/**
* Creates a builder for an annotation type
* @param type - The annotation class name
* @return Builder for configuring the annotation
*/
static AnnotationSpec.Builder builder(ClassName type);
/**
* Creates a builder for an annotation type
* @param type - The annotation class
* @return Builder for configuring the annotation
*/
static AnnotationSpec.Builder builder(Class<?> type);Usage Examples:
// From runtime annotation
@Deprecated
public class MyClass { }
Annotation deprecated = MyClass.class.getAnnotation(Deprecated.class);
AnnotationSpec spec = AnnotationSpec.get(deprecated);
// Build custom annotation
AnnotationSpec override = AnnotationSpec.builder(Override.class)
.build();
// Build annotation with members
AnnotationSpec column = AnnotationSpec.builder(ClassName.get("javax.persistence", "Column"))
.addMember("name", "$S", "user_name")
.addMember("nullable", "$L", false)
.build();Access the properties of an AnnotationSpec.
/**
* Returns the annotation type
* @return TypeName of the annotation
*/
TypeName type();
/**
* Returns the annotation members (attribute values)
* @return Map of member names to CodeBlock values
*/
Map<String, List<CodeBlock>> members();Convert to a builder for modification.
/**
* Creates a builder initialized with this annotation's properties
* @return Builder for modifying this AnnotationSpec
*/
AnnotationSpec.Builder toBuilder();The builder for configuring AnnotationSpec instances.
Add attribute values to the annotation.
/**
* Adds an annotation member from a format string
* @param name - The member name
* @param format - Format string for the value
* @param args - Arguments for format placeholders
* @return This builder for chaining
*/
AnnotationSpec.Builder addMember(String name, String format, Object... args);
/**
* Adds an annotation member from a CodeBlock
* @param name - The member name
* @param codeBlock - CodeBlock for the value
* @return This builder for chaining
*/
AnnotationSpec.Builder addMember(String name, CodeBlock codeBlock);Usage Examples:
// String member
AnnotationSpec annotation = AnnotationSpec.builder(MyAnnotation.class)
.addMember("value", "$S", "stringValue")
.build();
// Generates: @MyAnnotation("stringValue")
// Numeric member
AnnotationSpec timeout = AnnotationSpec.builder(Timeout.class)
.addMember("value", "$L", 30)
.build();
// Generates: @Timeout(30)
// Boolean member
AnnotationSpec column = AnnotationSpec.builder(Column.class)
.addMember("nullable", "$L", false)
.build();
// Generates: @Column(nullable = false)
// Class member
AnnotationSpec service = AnnotationSpec.builder(Service.class)
.addMember("value", "$T.class", UserService.class)
.build();
// Generates: @Service(UserService.class)
// Enum member
AnnotationSpec retention = AnnotationSpec.builder(Retention.class)
.addMember("value", "$T.$L", RetentionPolicy.class, "RUNTIME")
.build();
// Generates: @Retention(RetentionPolicy.RUNTIME)Build the final AnnotationSpec instance.
/**
* Builds the AnnotationSpec with configured members
* @return The built AnnotationSpec instance
*/
AnnotationSpec build();AnnotationSpec override = AnnotationSpec.builder(Override.class).build();
AnnotationSpec deprecated = AnnotationSpec.builder(Deprecated.class).build();
AnnotationSpec nullable = AnnotationSpec.builder(Nullable.class).build();When an annotation has a single member named "value", it can be specified without the name:
// Using the VALUE constant
AnnotationSpec suppressWarnings = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "unchecked")
.build();
// Generates: @SuppressWarnings("unchecked")
// Multiple values for array member
AnnotationSpec suppress = AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "unchecked")
.addMember("value", "$S", "rawtypes")
.build();
// Generates: @SuppressWarnings({"unchecked", "rawtypes"})AnnotationSpec column = AnnotationSpec.builder(Column.class)
.addMember("name", "$S", "user_id")
.addMember("unique", "$L", true)
.addMember("nullable", "$L", false)
.addMember("length", "$L", 50)
.build();
// Generates: @Column(name = "user_id", unique = true, nullable = false, length = 50)// String array
AnnotationSpec strings = AnnotationSpec.builder(MyAnnotation.class)
.addMember("values", "$S", "first")
.addMember("values", "$S", "second")
.addMember("values", "$S", "third")
.build();
// Generates: @MyAnnotation(values = {"first", "second", "third"})
// Class array
AnnotationSpec classes = AnnotationSpec.builder(ContextConfiguration.class)
.addMember("classes", "$T.class", ConfigA.class)
.addMember("classes", "$T.class", ConfigB.class)
.build();
// Generates: @ContextConfiguration(classes = {ConfigA.class, ConfigB.class})AnnotationSpec header = AnnotationSpec.builder(Header.class)
.addMember("name", "$S", "Accept")
.addMember("value", "$S", "application/json")
.build();
AnnotationSpec headers = AnnotationSpec.builder(Headers.class)
.addMember("value", "$L", header)
.build();
// Generates: @Headers(@Header(name = "Accept", value = "application/json"))
// Multiple nested annotations
AnnotationSpec header1 = AnnotationSpec.builder(Header.class)
.addMember("name", "$S", "Accept")
.addMember("value", "$S", "application/json")
.build();
AnnotationSpec header2 = AnnotationSpec.builder(Header.class)
.addMember("name", "$S", "User-Agent")
.addMember("value", "$S", "MyApp/1.0")
.build();
AnnotationSpec headerList = AnnotationSpec.builder(Headers.class)
.addMember("value", "$L", header1)
.addMember("value", "$L", header2)
.build();
// Generates: @Headers({
// @Header(name = "Accept", value = "application/json"),
// @Header(name = "User-Agent", value = "MyApp/1.0")
// })TypeSpec user = TypeSpec.classBuilder("User")
.addAnnotation(AnnotationSpec.builder(Entity.class)
.addMember("name", "$S", "users")
.build())
.addAnnotation(AnnotationSpec.builder(Table.class)
.addMember("name", "$S", "user_table")
.addMember("schema", "$S", "public")
.build())
.build();AnnotationSpec requestMapping = AnnotationSpec.builder(
ClassName.get("org.springframework.web.bind.annotation", "RequestMapping"))
.addMember("value", "$S", "/users")
.addMember("method", "$T.$L",
ClassName.get("org.springframework.web.bind.annotation", "RequestMethod"),
"GET")
.build();
MethodSpec getUsers = MethodSpec.methodBuilder("getUsers")
.addAnnotation(requestMapping)
.returns(ParameterizedTypeName.get(List.class, User.class))
.build();FieldSpec id = FieldSpec.builder(Long.class, "id", Modifier.PRIVATE)
.addAnnotation(Id.class)
.addAnnotation(AnnotationSpec.builder(GeneratedValue.class)
.addMember("strategy", "$T.$L", GenerationType.class, "IDENTITY")
.build())
.build();ParameterSpec userId = ParameterSpec.builder(String.class, "userId")
.addAnnotation(AnnotationSpec.builder(
ClassName.get("org.springframework.web.bind.annotation", "PathVariable"))
.addMember("value", "$S", "id")
.build())
.build();FieldSpec email = FieldSpec.builder(String.class, "email", Modifier.PRIVATE)
.addAnnotation(AnnotationSpec.builder(NotNull.class)
.addMember("message", "$S", "Email is required")
.build())
.addAnnotation(AnnotationSpec.builder(Email.class)
.addMember("regexp", "$S", "^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$")
.build())
.addAnnotation(AnnotationSpec.builder(Size.class)
.addMember("min", "$L", 5)
.addMember("max", "$L", 100)
.build())
.build();FieldSpec name = FieldSpec.builder(String.class, "name", Modifier.PRIVATE)
.addAnnotation(AnnotationSpec.builder(
ClassName.get("com.fasterxml.jackson.annotation", "JsonProperty"))
.addMember("value", "$S", "full_name")
.addMember("required", "$L", true)
.build())
.build();// Define annotation type
TypeSpec myAnnotation = TypeSpec.annotationBuilder("MyAnnotation")
.addModifiers(Modifier.PUBLIC)
.addMethod(MethodSpec.methodBuilder("value")
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.returns(String.class)
.defaultValue("$S", "default")
.build())
.addMethod(MethodSpec.methodBuilder("count")
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.returns(int.class)
.defaultValue("$L", 0)
.build())
.build();
// Use the custom annotation
AnnotationSpec usage = AnnotationSpec.builder(
ClassName.get("com.example", "MyAnnotation"))
.addMember("value", "$S", "custom")
.addMember("count", "$L", 42)
.build();The AnnotationSpec.Builder class implements visitor methods for processing AnnotationMirror values during annotation processing. These methods are used internally by the get(AnnotationMirror) factory method and are rarely called directly by users.
/**
* Visits a nested annotation value
* @param a - The annotation mirror
* @param name - The member name
* @return This builder for chaining
*/
Builder visitAnnotation(AnnotationMirror a, String name);
/**
* Visits an enum constant value
* @param c - The enum constant variable element
* @param name - The member name
* @return This builder for chaining
*/
Builder visitEnumConstant(VariableElement c, String name);
/**
* Visits a class/type value
* @param t - The type mirror
* @param name - The member name
* @return This builder for chaining
*/
Builder visitType(TypeMirror t, String name);
/**
* Visits an array of annotation values
* @param values - List of annotation values
* @param name - The member name
* @return This builder for chaining
*/
Builder visitArray(List<? extends AnnotationValue> values, String name);Note: These methods are part of the AnnotationValueVisitor pattern used for javax.lang.model integration. Normal users should use the AnnotationSpec.get(AnnotationMirror) factory method or the addMember() methods instead of calling these visitor methods directly.
/**
* Constant for the "value" member name
* Used when referring to the default annotation member
*/
public static final String VALUE = "value";class AnnotationSpec {
static final String VALUE = "value";
static AnnotationSpec get(Annotation annotation);
static AnnotationSpec get(Annotation annotation, boolean includeDefaultValues);
static AnnotationSpec get(AnnotationMirror annotation);
static Builder builder(ClassName type);
static Builder builder(Class<?> type);
TypeName type();
Map<String, List<CodeBlock>> members();
Builder toBuilder();
boolean equals(Object o);
int hashCode();
String toString();
}
class AnnotationSpec.Builder {
AnnotationSpec.Builder addMember(String name, String format, Object... args);
AnnotationSpec.Builder addMember(String name, CodeBlock codeBlock);
AnnotationSpec build();
// Visitor methods for javax.lang.model integration (rarely used directly)
Builder visitAnnotation(AnnotationMirror a, String name);
Builder visitEnumConstant(VariableElement c, String name);
Builder visitType(TypeMirror t, String name);
Builder visitArray(List<? extends AnnotationValue> values, String name);
}Install with Tessl CLI
npx tessl i tessl/maven-com-palantir-javapoet--javapoetdocs