Skip to main content
The Java SDK instruments methods with @VPoint. Three pieces cooperate: the annotation processor generates schema-bearing aggregate classes at compile time, the varianz-agent intercepts annotated methods at class-load time, and RegistryBinding connects the process to the registry. The Gradle plugin wires all three.

Canonical imports

These are the only correct paths — io.varianz.annotations.VPoint (missing the vpoint package), io.varianz.sdk.VPoint, and similar guesses do not exist.

Defining VPoints

  • Always set an explicit name. Omitted names derive from package/class/method names and break under refactoring.
  • Any class and method visibility works — the agent rewrites bytecode, so private/final/synchronized methods are fine. (One limit: for private inner classes, the generated aggregate can’t expose direct typed invocation; stages by name still work.)
  • Plain beans and public-field DTOs are introspected automatically — fields from getters or public fields, constructors matched by type compatibility (static factories like Quote.of(...) included). No @Struct needed for typical types. Field names convert camelCase → snake_case in CEL (basePricebase_price).
  • For gRPC handlers, extract business logic into a plain-typed @VPoint method — keep StreamObserver out of schemas. See the pattern.
The processor generates a PricingServiceVPoints aggregate in the same package, wrapping the method with stage execution and embedding the VPD schema.

How interception works

The agent transforms each @VPoint method at class load: the original body is cloned to <method>$varianzDirect (keeping line tables, so breakpoints still land in your source), and the method itself becomes a dispatch stub into the stage chain. Calling the method normally routes through the pipeline — no bind()-proxy calls needed:
With no stages registered, dispatch reads one volatile and calls the clone directly — the no-stage path is essentially free after JIT warmup. If the agent is not attached, the SDK prints one loud warning at first class load and @VPoint methods run their original bodies. The Gradle/Maven plugins attach the agent for test and run tasks; production launchers must attach it explicitly:

Connecting to the registry

Call RegistryBinding.init() early — before your server starts serving — then bind() each service instance to register its VPDs:
In Spring Boot, do both in a @PostConstruct. Session propagation uses the pull-based @VarianzId @Function(global = true) pattern — the full setup is in Propagate sessions.

Control annotations

Aggregate.bind(service) returning a typed proxy exists for advanced cases (test harnesses, programmatic stage composition) — normal code doesn’t need it.

Disabling interception

In order of preference: don’t attach the agent (Gradle weave.set(false), Maven -Dvarianz.skipAgent=true); disable at runtime with -Dvarianz.proc.weave=false; or opt out one method with @VPoint(wrapped = false).

Maven

On Java 22+, append --enable-native-access=io.varianz.native_loader to the <argLine>. To disable interception for a build, pass -Dvarianz.skipAgent=true.

Native access (Java 22+)

The SDK loads its Rust runtime via System.load, a restricted method under JEP 472. All Varianz JNI loading funnels through the io.varianz.native_loader module, so one flag covers the SDK:
The Gradle plugin injects it automatically for Test/JavaExec on Java 22+; Maven and hand-rolled launchers add it to their JVM args. Prefer this targeted grant over ALL-UNNAMED.

Troubleshooting

  • @VPoint methods not intercepted — almost always the agent isn’t attached; look for the [varianz] Varianz agent not attached warning. Check weave (Gradle) or the prepare-agent execution + @{varianz.agentArgs} (Maven). For custom launchers, verify with -Dvarianz.proc.debug=true (the agent logs its transformer registration).
  • NoClassDefFoundError for a ...VPoints class at first call — the annotation processor didn’t run at compile time, so the aggregate doesn’t exist. With Gradle, compileJava should log Note: [Varianz] Generated adapters....
  • System.load warnings on Java 24+ — the JVM fork is missing the native-access flag; see above.