Method decorators
Combine before/around/after slots in a single processor to stack several method-level annotations.
BasicStaticProxyAnnotationProcessor provides three composition slots for
method-level annotations: beforeDelegation, aroundDelegation and
afterDelegation. They let one processor handle several annotations on the
same method without forking each combination by hand.
The three slots
@Override
protected List<CodeBlock> beforeDelegation(ExecutableElement methodElement,
String methodName2Delegate,
TypeElement targetType) {
if (hasAnnotation(methodElement, RequiresRole.class.getCanonicalName())) {
return List.of(CodeBlock.of("securityEnforcer.requireRole();\n"));
}
return List.of();
}
@Override
protected Optional<CodeBlock> aroundDelegation(ExecutableElement methodElement,
String methodName2Delegate,
TypeElement targetType) {
return Optional.of(
CodeBlock.of("$L;\n",
delegatorStatementWithReturn(methodElement, methodName2Delegate)));
}
@Override
protected List<CodeBlock> afterDelegation(ExecutableElement methodElement,
String methodName2Delegate,
TypeElement targetType) {
return List.of(CodeBlock.of("securityAudit.recordSuccess();\n"));
}
beforeDelegation returns blocks to emit before the delegate call.
aroundDelegation returns the delegate call itself (with the return value if
any). afterDelegation returns blocks to emit after the delegate call.
The generated method body is the concatenation of all three. Each slot is ordered, so you can return multiple blocks per slot to stack contributions from different annotations on the same method.
Composing several annotations
A typical security-oriented target stacks multiple method-level annotations:
public interface AccountService {
@RequiresPolicy("account")
@RequiresPermission("account:read")
@RequiresRole("admin")
Account loadAccount(String accountId);
}
Inside beforeDelegation you check for each annotation in turn and append
its enforcement block. The generated method ends up with:
@Override
public Account loadAccount(String accountId) {
securityEnforcer.requirePolicy("account");
securityEnforcer.requirePermission("account:read");
securityEnforcer.requireRole("admin");
Account result = delegator.loadAccount(accountId);
securityAudit.recordSuccess();
return result;
}
Annotation lookup helpers
The base class provides:
annotationsOn(ExecutableElement)— all annotations on the method.annotationsOn(ExecutableElement, Set<String>)— only those whose fully qualified names are in the set.hasAnnotation(ExecutableElement, String)— boolean check by FQCN.
Use these inside the three decorator slots to gate which blocks you emit.
When to use aroundDelegation
The default behaviour is “call the delegate, return its result.” If you only
want before/after, leave aroundDelegation at the default (do not override
it). Override it when you need to wrap the call in something — a try/finally,
a Context.run(() -> ...), a fallback on exception.