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
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
) {
/* */
}
@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;
}
/* */
}
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 ServiceContainer
and use a delegated property, such as:
private val helpCommand: IHelpCommand by serviceContainer.lazy()
Request a LazyService
with the element type being the requested service,
and then get the service when needed by using getValue()
.
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
.
Optional lazy services
When you are about to request the service from LazyService
,
you can use canCreateService or getServiceError
to check if it is available at this point in time.