BService
Marks this class as a service, or this function as a service factory. Can be added on your own annotation.
Service factories must be static, or declared in a service class, or an object
.
Warning: Top-level functions are not processed, you must have them in an object/class.
Service retrieval
Classes annotated with @BService (either directly or via an intermediary annotation) can be retrieved.
In most cases, the services you retrieve will be type-matched, however, here is how they are looked up:
If @ServiceName is used: Finds a service with the same name and satisfying type.
If a parameter name is available (native in Kotlin, requires the
-parameters
compiler arg in Java): Finds a service with the same name and a satisfying type.In other cases, a service with a satisfying type will be returned.
Primary providers
When requesting a service of a specific type, there must be at most one service provider for such a type.
If multiple usable providers for the same type are present, no service can be returned unless one primary provider is defined.
For example, if you have two service factories with the same return type:
✗ If both are usable
✓ One has a failing condition, meaning you have one usable provider
✓ One is annotated with @Primary, in which case this one is prioritized
Note: You can still get all the types / instances.
Service types
Registered services cannot be retrieved by every type they represent, i.e., they can only be retrieved by the types they are explicitly registered as:
Classes annotated as services are registered with the class type.
Service factories (including property getters) register their returned services as the function's return type.
In both cases, supertypes can be added using @ServiceType
Using service factories, if your function creates an implementation class, but returns the abstract class/interface, the service will only be retrievable by the latter.
Note: Annotated functions in subclasses of services returned by service factories cannot be processed. For example, if you have a @BEventListener in your implementation class, but your service factory returns the abstract class/interface, then your listener will not work.
Interfaced services
Request a List will retrieve all services implementing the list's element type, such as List<ApplicationCommandFilter<*>>
.
Lazy services
In Kotlin, you can request a ServiceContainer and use a delegated property, such as private val helpCommand: IHelpCommand by serviceContainer.lazy()
.
In both Kotlin and Java, you can request a LazyService with the requested service type and get the value with LazyService.getValue().
Note: Lazy services cannot hold a list of interfaced services, nor can a list of lazy services be requested.
Optional services
If you want to get a service only if it is available, you can use Kotlin's nullable / optional parameters, but Java users will need a runtime-retained @Nullable annotation (such as javax.annotation.Nullable, or, in checker-framework or JSpecify) or @Optional.
Lazy services can also have their element type be marked as nullable, for example, Lazy<@Nullable IHelpCommand>
.
If a service is not available and the parameter is required, an exception will be thrown.
Naming scheme
By default, the services are named as such:
For classes, the class's nested name but with the first letter lowercase. (e.g., a
ChannelResolver
inside aChannelResolverFactory
=>channelResolverFactory.ChannelResolver
)For methods, the method's name.
Loading order
By default, the service is eagerly loaded at startup, when it is in the framework's classpath, but you can make the service loaded only when requested, by using @Lazy.
Note: The service can be loaded early if it has an event listener, is a command, autocomplete, a modal handler, etc...
Note 2: Service factories are prioritized over class annotations, see ServicePriority for more details.
See also
@InjectedService
@ConditionalService
@InterfacedService
@Dependencies
@Lazy
@Primary
@ServiceType
@ServiceName
@ServicePriority