Retrieving services
Any class given by a service provider can be injected into other service providers, requesting a service is as simple as declaring a parameter in the class's constructor, or the service factory's parameters.
Named services can be retrieved by using @ServiceName
on the parameter,
this can be omitted if the parameter name matches a service with a compatible type.
Tip
You can also get services manually with BContext
or ServiceContainer
, the latter has all methods available,
including Kotlin extensions.
Example
@BService // Enables the service to request services and be requested
public class TagDatabase { /* */ }
@Command // Enables the command to request services and be requested
public class TagCommand {
private final Component components;
private final TagDatabase tagDatabase;
public TagCommand(
// You can even request framework services, as long as they are annotated with @BService or @InterfacedService
Component components,
// and your own services
TagDatabase tagDatabase
) {
this.components = components;
this.tagDatabase = tagDatabase;
}
/* */
}
@BService // Enables the service to request services and be requested
class TagDatabase { /* */ }
@Command // Enables the command to request services and be requested
class TagCommand(
// You can even request framework services, as long as they are annotated with @BService or @InterfacedService
// Here I've named it "componentsService" because "components" might conflict with some JDA-KTX builders
private val componentsService: Components,
// and your own services
private val tagDatabase: TagDatabase
) {
/* */
}
Retrieving services by name
Consider the following service providers:
@BService
class HttpClientProvider {
// @Primary // This is only needed if you try to get an OkHttpClient without matching the name
@BService
fun httpClient(): OkHttpClient = OkHttpClient()
@BService
fun cachedHttpClient(
// Inject the default http client (declared above)
// This is not the same as calling the method! as it would create 2 different clients
// This would not work if the parameter was named differently,
// unless @Primary was used on the default declaration above
httpClient: OkHttpClient
): OkHttpClient {
val tempDirectory = Files.createTempDirectory(null).toFile()
return httpClient.newBuilder()
.cache(Cache(tempDirectory, maxSize = 1024 * 1024))
.build()
}
}
@BService
class MyApi(@ServiceName("cachedHttpClient") httpClient: HttpClient)
@BService
class MyApi(private val cachedHttpClient: HttpClient)
@BService
public class HttpClientProvider {
// @Primary // This is only needed if you try to get an OkHttpClient without matching the name
@BService
public OkHttpClient httpClient() {
return new OkHttpClient();
}
@BService
public OkHttpClient cachedHttpClient(
// Inject the default http client (declared above)
// This is not the same as calling the method! as it would create 2 different clients
// This would not work if the parameter was named differently,
// unless @Primary was used on the default declaration above
OkHttpClient httpClient
) throws IOException {
final File tempDirectory = Files.createTempDirectory(null).toFile();
return httpClient.newBuilder()
.cache(new Cache(tempDirectory, 1024 * 1024))
.build();
}
}
@BService
public class MyApi {
public MyApi(@ServiceName("cachedHttpClient") HttpClient httpClient) {
// ...
}
}
@BService
public class MyApi {
public MyApi(HttpClient cachedHttpClient) {
// ...
}
}
Warning
For this to work, you need to enable Java parameter names
Primary providers¶
When requesting a service of a specific type/name, there must be at most one usable service provider.
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 retrieve existing services with ServiceContainer#getInterfacedServices/getInterfacedServiceTypes
Interfaced services¶
A list which the element type is an interfaced service can be requested, the list will then contain all instantiable instances with the specified type.
Example
List<ApplicationCommandFilter<?>>
will contain all instances implementing ApplicationCommandFilter
,
which are usable.
Lazy services¶
Lazy service retrieval enables you to get lazily-created service, delaying the initialization,
or to get services that are not yet available, such as manually injected services (like JDA
).
Retrieving a lazy service
Request a Lazy
with the element type being the requested service,
and then get the service when needed by using Lazy#getValue
.
Request a ServiceContainer
and use a delegated property, such as:
private val helpCommand: IHelpCommand by serviceContainer.lazy()
Note
Lazy injections cannot contain a list of interfaced services, nor can a list of lazy services be requested.
Optional services¶
When a requested service is not available, and is not a soft-dependency, service creation will fail.
In case your service does not always require the service,
you can prevent failure by using 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 nullability
Lazy services can also have their element type be marked as nullable,
for example, Lazy<@Nullable IHelpCommand>
.