Getting started (0.1.0-rc.4, experimental)¶
SpringGraph is an annotation processor. It checks a supported subset of Spring constructor injection while your code compiles. It does not change your runtime: Spring still constructs every bean. Nothing needs to be on the runtime classpath.
Requirements¶
- Java 17 or newer for compilation.
- Spring Framework 6.2 / Spring Boot 3.5, or Spring Framework 7 / Spring Boot 4.1. Those are the versions it has been tried with.
- Java sources in a single module. Kotlin is not supported.
1. Get the processor¶
The processor is io.github.mrsluffy:springgraph-processor. Releases are on Maven Central, GitHub Packages and
GitHub Releases; see the
release history for the available versions.
- Maven Central (recommended): no setup. Maven uses it by default; in Gradle, declare
mavenCentral(). - GitHub Packages: only needed if you cannot use Maven Central. Add the repository
https://maven.pkg.github.com/mrsluffy/spring-graph(the owner must be lowercase). GitHub requires a personal access token (classic) withread:packages, even for public packages. In Gradle, put GitHub Packages beforemavenCentral()so Gradle tries it first for the processor:
repositories {
maven {
url = uri("https://maven.pkg.github.com/mrsluffy/spring-graph")
credentials {
username = providers.gradleProperty("gpr.user").getOrNull() ?: System.getenv("USERNAME")
password = providers.gradleProperty("gpr.key").getOrNull() ?: System.getenv("TOKEN")
}
}
mavenCentral()
}
Put gpr.user=<your GitHub username> and gpr.key=<token> in ~/.gradle/gradle.properties, never in the project.
For Maven, add this repository to your pom.xml:
<repositories>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/mrsluffy/spring-graph</url>
</repository>
</repositories>
Add a <server> with the matching github ID, your GitHub username and token to ~/.m2/settings.xml, as described in
GitHub's Maven registry docs.
- From source: in a checkout of this revision, install into your local Maven repository:
./gradlew :springgraph-processor:publishToMavenLocal
This installs io.github.mrsluffy:springgraph-processor:0.1.0-rc.4 (the version in gradle.properties) into
~/.m2/repository. The group and version depend on the checkout; check build.gradle.kts and gradle.properties
when using another revision. In Gradle, put mavenLocal() before mavenCentral() to prefer the locally built artifact.
2. Add it to your build¶
The examples use the version in this guide's title. For a source installation, use the version you installed. Start in advisory mode. Findings are warnings and the build always passes.
Gradle (Kotlin DSL)¶
repositories {
mavenCentral()
}
dependencies {
annotationProcessor("io.github.mrsluffy:springgraph-processor:0.1.0-rc.4")
}
tasks.named<JavaCompile>("compileJava") {
options.compilerArgs.addAll(listOf(
"-Aspringgraph.mode=advisory",
"-Aspringgraph.runtimeBoundaries="
))
}
Configure only compileJava. Test sources usually have test-only wiring that would produce noise.
Maven¶
Add to maven-compiler-plugin's configuration. If you already use annotationProcessorPaths (Lombok, MapStruct), add SpringGraph to
the same list. The order does not matter.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<!-- existing processors, e.g. Lombok -->
<path>
<groupId>io.github.mrsluffy</groupId>
<artifactId>springgraph-processor</artifactId>
<version>0.1.0-rc.4</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-Aspringgraph.mode=advisory</arg>
<arg>-Aspringgraph.runtimeBoundaries=</arg>
</compilerArgs>
</configuration>
</plugin>
Once annotationProcessorPaths is set, Maven only runs the processors listed there. If a processor used to be discovered from
the classpath, list it too.
3. Read the report¶
Compile, then open META-INF/springgraph/graph.txt in the class output
(build/classes/java/main for Gradle, target/classes for Maven):
SpringGraph report (mode=advisory)
com.example.OrderService
com.example.PaymentGateway -> com.example.DefaultPaymentGateway [validated]
com.example.OrderRepository -> Spring Data repository, created at runtime [not statically validated]
com.fasterxml.jackson.databind.ObjectMapper -> <missing> [unresolved]
...
Coverage: 1 of 3 dependencies statically validated
The same directory also has graph.json, a machine-readable version for tools. In IntelliJ
IDEA, the SpringGraph plugin shows it as gutter icons and a tool window. The plugin can also run its own
bundled copy of the processor, to try SpringGraph on a project before changing its build.
4. Declare runtime boundaries¶
A <missing> dependency that Spring does supply (Boot auto-configuration, a @Bean method, another module) is a runtime
boundary. Declare its fully qualified type, comma-separated:
-Aspringgraph.runtimeBoundaries=com.fasterxml.jackson.databind.ObjectMapper,javax.sql.DataSource
Boundaries are listed as [not statically validated] in the report. A test that starts the application context is still the only
check that they exist. Misspelled or unused boundaries are reported as SG009.
You do not need to declare:
- Spring Data repository interfaces (recognized automatically, never counted as validated);
@Valueparameters (property values, not beans).
5. Switch to strict¶
When the advisory build shows no findings you disagree with, set -Aspringgraph.mode=strict. Findings become compile errors.
What is checked, and what is not¶
Checked: classes annotated directly with @Component, @Service, @Repository, @Controller or @RestController that have exactly
one non-private constructor, including Lombok-generated ones. Missing, ambiguous and cyclic constructor dependencies are reported.
Not checked (reported as not validated, never guessed): custom and @Named qualifiers, @Priority, generics and collections, @Lazy, providers, profile and
conditional components, non-singleton scopes, @Bean methods, field and setter injection, other modules. See
support matrix and diagnostics.
Build impact¶
On two small Gradle projects the clean-compile overhead was within run-to-run noise; on a 190-file project, annotation processing alone took about 400 ms longer (median). Large projects and full builds were not measured. The processor is not incremental, so Gradle recompiles the whole module when it is enabled. See performance.
Turning it off¶
Remove -Aspringgraph.mode. Without it the processor does nothing.