Avaje SPI
Discord | Source | API Docs | Issues | Releases |
---|---|---|---|---|
Discord | Github | Javadoc | Github |
The Java Service Provider
Interface uses configuration files to
find and load concrete implementations of given
service provider interfaces. The downside of this system is that
configuration files are not checked by the java
compiler.
As such it is a common occurence to forget to add the correct entries.
This zero-dependency library uses annotation processing to automatically
generate the required META-INF/services
entries for
annotated classes. In addition, it will validate that the required
provides clauses are present in an application's module-info
files.
Dependency
To use this library add the avaje-spi-service as a provided/optional dependency. (The dependencies need only be included at compile-time and are not required at runtime)
<dependency>
<groupId>io.avaje</groupId>
<artifactId>avaje-spi-service</artifactId>
<version>1.2</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
JDK 23+ note:
In JDK 23+, annotation processors are disabled by default, so we need to add a compiler property to re-enable.
<properties>
<maven.compiler.proc>full</maven.compiler.proc>
</properties>
@ServiceProvider
On classes that you'd like registered, place the
@ServiceProvider
annotation.
As long as you only have one interface/ superclass, that type is
assumed to be the SPI interface. So given the
example below:
@ServiceProvider
public class BassServiceProvider implements MusicService {
...
}
Multi-Interfaced classes
If you have multiple interfaces and/or a base type, the library cannot infer the contract type. In such a case, we
specify the contract type explicitly in the @ServiceProvider
:
@ServiceProvider(MusicService.class)
public class BassServiceProvider extends Instrument implements Stringed, MusicService {
...
}
Module Validation
For modular projects, the processor will validate that all the required provides clauses are accounted for. A compile error describing what `provides` statements are missing will be throw if validations fail. So given the following class and module-info:
@ServiceProvider
public class BassServiceProvider implements MusicService {
...
}
module my.module {
requires static io.avaje.spi;
//provides not defined
}
You will get the compile error:
Compilation failure /src/main/java/module-info.java:[1,1] Missing `provides MusicService with BassServiceProvider;`
@Service
As a library developer, should you wish to define an spi interface for multiple layers of extension, the
@Service
annotation will help ensure those using @ServiceProvider
will automatically register the correct top-level type.
Consider the example of avaje-validaton:
/** Super interface for all validation SPIs */
@Service
public sealed interface ValidationExtension
permits AdapterFactory,
AnnotationFactory,
GeneratedComponent,
MessageInterpolator,
ValidatorCustomizer {}
To reduce calls to the ServiceLoader
, avaje-validation only loads the top level ValidationExtension
interface instead of loading the individual interfaces.
The presence of the @Service
annotation ensures the below will be registered correctly.
//this class is registered as a ValidationExtension rather than a ValidatorCustomizer
@ServiceProvider
public class Customizer implements ValidatorCustomizer {
....
}