Static Proxies

Annotation-driven proxy classes generated during compilation. Zero reflection at runtime.

ProxyBuilder ships a set of annotation processors that generate proxy classes during javac. The generated classes wrap your interface and delegate to a real implementation, with cross-cutting behaviour baked in at compile time.

The five annotations

AnnotationGenerated class wraps the target with…
@StaticLoggingProxyMethod-call logging via the project logger
@StaticMetricsProxyDropwizard timings per method
@StaticVirtualProxyLazy instance creation strategy
@StaticObjectAdapterFunctional-interface adapter slots per method
@DynamicObjectAdapterBuilderAdapter builder with setOriginal / withXxx API

You put the annotation on the interface that you want to proxy. The processor generates a class named after the interface plus a suffix (for example, ServiceStaticLoggingProxy). Your code instantiates the generated class and tells it which implementation to delegate to.

Why generated, not reflective

  • No reflection at runtime. The generated class calls the real implementation through a direct method call. There is no Method.invoke(...).
  • GraalVM native image friendly. No reflective entries to configure.
  • Smaller startup cost. No proxy class is synthesised at the moment of use; it exists on disk after javac.
  • You can read it. The generated source goes into target/generated-sources/annotations/ (Maven default). Open it, read it, step through it.

Compile-time safety

The processors will fail your build for shapes they cannot safely proxy:

  • final classes annotated for static proxy generation.
  • final methods on a target type.
  • private methods on a target type.
  • static methods (unless -Aproxybuilder.failOnStaticMethods=false).

See Processor safety rules for the full list and the reasoning.

Combining annotations

You can put more than one annotation on the same interface. Each processor generates its own class. You pick which generated class to use at the point of construction.

If you need to combine behaviour within a single generated class — for example logging and metrics on the same proxy — write your own processor. See Custom Processors.

In this section

Edit this page on GitHub ↗