Basics
The minimum viable annotation processor on top of BasicStaticProxyAnnotationProcessor.
A custom processor extends BasicStaticProxyAnnotationProcessor<T> where T
is the annotation you handle. You implement three things: which annotation
you are responsible for, what extra class-level bits you want, and how each
method body looks.
The smallest useful processor
public final class SecuredAnnotationProcessor
extends BasicStaticProxyAnnotationProcessor<Secured> {
@Override
public Class<Secured> responsibleFor() {
return Secured.class;
}
@Override
protected void addClassLevelSpecs(TypeElement typeElement, RoundEnvironment roundEnv) {
// Add fields, marker annotations, helper methods, or constructor details.
}
@Override
protected CodeBlock defineMethodImplementation(ExecutableElement methodElement,
String methodName2Delegate,
TypeElement targetType) {
return CodeBlock.builder()
.addStatement(delegatorStatementWithReturn(methodElement, methodName2Delegate))
.build();
}
}
responsibleFor() tells the base class which annotation to discover.
addClassLevelSpecs is your hook for adding state at class level — a field,
a marker annotation, a helper. defineMethodImplementation runs once per
method on the target interface and returns the generated body.
Choosing the generated class suffix
By default the generated class name is the target name plus a suffix derived from the annotation type. Override it programmatically:
@Override
protected String generatedClassSuffix(TypeElement typeElement) {
return "Secured";
}
Or use the processor option globally:
-Aproxybuilder.suffix=Secured
Constructor modifiers
If you do not want a public constructor on the generated class:
@Override
protected Set<Modifier> filterConstructorModifiers(Set<Modifier> modifiers) {
modifiers.remove(Modifier.PUBLIC);
modifiers.add(Modifier.PROTECTED);
return modifiers;
}
Static imports
addStaticImports(JavaFile.Builder) is a no-op by default. Override it only
when the generated source needs static imports:
@Override
protected void addStaticImports(JavaFile.Builder builder) {
builder.addStaticImport(MySecurityApi.class, "requireRole");
}
Useful helpers on the base
BasicAnnotationProcessor (the parent of the static-proxy base) exposes:
pkgName(TypeElement)— package name of the target type.className(Element)— simple class name.targetClassNameSimpleForGeneratedClass(TypeElement)— name of the generated class.targetClassNameSimpleForSourceClass(TypeElement)— name of the source class.defineDelegatorField(TypeElement)— the standard delegator field declaration.defineSimpleClassNameField(TypeElement)— a static class-name field for logging.defineParamsForMethod(ExecutableElement)— JavaPoetParameterSpecs for a method.extractArgumentTypeListFromMethod(ExecutableElement)— raw parameter type mirrors.delegatorMethodCall(ExecutableElement, String)— text of the delegator call (no statement terminator).delegatorStatementWithReturn(ExecutableElement, String)—return delegator.method(...);delegatorStatementWithOutReturn(ExecutableElement, String)—delegator.method(...);delegatorStatementWithLocalVariableResult(ExecutableElement, String)—Type result = delegator.method(...);error(Element, String),warning(...),note(...)— compiler diagnostics throughMessager.
Discoverability
Annotation processors are discovered through
META-INF/services/javax.annotation.processing.Processor. Add your
processor’s fully qualified name to that file.
Next
- Method decorators — combine before/around/after on the same method.
- Processor options — declare and read compiler arguments.