Skip to content

Graph boundaries (prototype decisions)

Opt-in

The processor does nothing unless a mode is supplied as a compiler option:

Option Meaning
-Aspringgraph.mode=advisory All findings are warnings. Missing bindings are "suspected".
-Aspringgraph.mode=strict Findings are errors. The module is treated as a closed static boundary.
-Aspringgraph.runtimeBoundaries=a.B,c.D Fully qualified types Spring supplies at runtime.

Why options and not annotations: no annotations module, no runtime classpath change, and boundaries are centralized in build configuration. Revisit after the pilot (Phase 4).

Classification

Result Report text Diagnostic
Resolved [validated] none
Declared boundary [not statically validated] none (report only)
Spring Data repository Spring Data repository, created at runtime [not statically validated] none (report only)
@Value parameter @Value property/expression [not a bean, not validated] none
Missing <missing> SG001
Ambiguous <ambiguous: ...> SG007
Unsupported [unsupported, not validated] SG004/5/6/8

A boundary only applies when no static candidate exists. An undeclared unknown type is never silently treated as a boundary.

Decision: conditional candidates are never resolved

Found by the spring-petclinic-rest pilot. If any component that could satisfy a dependency carries @Profile or a @Conditional* annotation, the processor does not resolve, rank, or call the dependency ambiguous: which candidate is active depends on the environment. The dependency is SG006 (unsupported) unless its type is explicitly declared a runtime boundary, in which case the declaration wins. The cost is one boundary declaration per profile-switched interface (6 of 8 declarations in that pilot).

Boundary declaration checks (SG009)

A declared boundary that does nothing is reported, because a silently ignored declaration hides a missing binding:

Situation Severity Message gist
Type not on the compilation classpath (typo, removed class) Error in strict, warning in advisory "not found on the compilation classpath", with a "Did you mean" suggestion from the requested types (simple-name edit distance up to 3)
Type exists but no analysed component requests it Warning "never requested ... remove if stale"
Type is requested but every such dependency is resolved statically or classified by another rule Warning "does not apply to any dependency"

A typo never replaces the real finding: the unresolved dependency still gets its SG001.

Low-coverage note

When fewer than half of the dependencies are statically validated, the build emits a single compiler NOTE pointing at the report. It is not a warning, and not shown at 50% or above.

Decision: Spring Data repositories are recognized, not declared

An interface extending org.springframework.data.repository.Repository (and not @NoRepositoryBean) is classified as its own kind, not as an "unknown type turned boundary". The rule is narrow and checkable from the type hierarchy. It is still never counted as validated, because whether Spring Data actually creates the bean depends on repository scanning and conditions the processor cannot see. This keeps the "no automatic unknown-is-external" policy intact. Revisit if it produces false negatives (for example repositories excluded from scanning); the pilot is the only evidence so far.

Round handling

Components are collected by name in every round. Analysis runs once, in the final round, so types produced by other processors are visible. Components are re-resolved by name at that point; no compiler objects are kept across rounds. Messages in the final round are still reported by javac as errors.

Incremental compilation

The processor is not declared incremental. Gradle falls back to full recompilation of the module. Incremental behavior is untested and not claimed.