Creating services
To register a class as a service, add @BService to your class declaration.
@BService is the base annotation to register a service, 
other annotations exist such as @Command and @Resolver, 
but the appropriate documentation will specify if such alternatives are required.
Info
All classes available for dependency injection must be in the framework's classpath,
by adding packages to BConfigBuilder#packages, or by using BConfigBuilder#addSearchPath, 
all classes are searched recursively.
Service factories¶
Service factories are methods that create initialized services themselves, they accept other services as parameters and define a service with the method's return type.
In addition to the package requirement,
they must be annotated with @BService, be in a service, or in an object, or be a static method.
Terminology
Classes registered as services, and service factories, are service providers.
Example
public class Config {
    private static Config INSTANCE = null;
    /* */
    // Service factory, registers as "Config" (as it is the return type), with the name "config"
    // You can use any method name, but the method name is what the service is registered as
    @BService
    public static Config config() {
        if (INSTANCE == null) {
            // Of course here you would load the config from a file
            INSTANCE = new Config();
        }
        return INSTANCE;
    }
}
class Config {
    /* */
    companion object {
        // Service factory, registers as Config (as it is the return type), with the name "config"
        // You can use any method name, but the method name is what the service is registered as
        @BService
        fun config(): Config {
            // Of course here you would load the config from a file
            Config()
        }
    }
}
class Config {
    /* */
    companion object {
        // Service factory, registers as Config (as it is the return type), with the name "config"
        @get:BService
        val config: Config by lazy {
            // Of course here you would load the config from a file
            Config()
        }
    }
}
Tip
To suppress unused warnings on the declaring class, you can use @BConfiguration,
unless the declaring class itself must be a service.
Conditional services¶
Warning
This section only applies to the built-in dependency injection
Some services may not always be instantiable, some may require soft dependencies (prevents instantiation if a service is unavailable, without failing), while some run a set of conditions to determine if a service can be instantiated.
Services that are not instantiable will not be created at startup, will be unavailable for injection and do not figure in the list of interfaced services.
Info
All the following annotations must be used alongside a service-declaring annotation, 
such as @BService or @Command.
Tip
There is a few built-in conditional annotations, such as:
- @RequiredIntents: Enables a service if the- JDAServicehas the specified intents enabled.
- @RequiresDatabase: Enables a service if a- (Blocking)Databaseinstance is available.
- @RequiresComponents: Enables a service if components are enabled.
Dependencies¶
The @Dependencies annotation lets you define soft dependencies,
that is, if any of these classes in the annotation are unavailable, your service will not be instantiated.
Without the annotation, any unavailable dependency would throw an exception.
Interfaced conditions¶
@ConditionalService defines a list of classes implementing ConditionalServiceChecker,
the service is only created if none of these classes return an error message.
ConditionalServiceChecker can be implemented on any class that has a no-arg constructor, or is an object.
Example
@Command
@ConditionalService(TagCommand.FeatureCheck.class) // Only create the command if this passes
public class TagCommand {
    /* */
    public static class FeatureCheck implements ConditionalServiceChecker {
        @Nullable
        @Override
        public String checkServiceAvailability(@NotNull ServiceContainer serviceContainer, @NotNull Class<?> checkedClass) {
            final var config = serviceContainer.getService(Config.class); // Suppose this is your configuration
            if (!config.areTagsEnabled()) {
                return "Tags are disabled in the configuration"; // Do not allow the tag command!
            }
            return null; // No error message, allow the tag command!
        }
    }
}
@Command
@ConditionalService(TagCommand.FeatureCheck::class) // Only create the command if this passes
class TagCommand {
    /* */
    object FeatureCheck : ConditionalServiceChecker {
        override fun checkServiceAvailability(serviceContainer: ServiceContainer, checkedClass: Class<*>): String? {
            val config = serviceContainer.getService<Config>() // Suppose this is your configuration
            if (!config.enableTags) {
                return "Tags are disabled in the configuration" // Do not allow the tag command!
            }
            return null // No error message, allow the tag command!
        }
    }
}
Annotation conditions¶
@Condition is a meta-annotation (an annotation for annotations) which marks your own annotation as being a condition.
Similar to interfaced conditions, they must refer to an implementation of CustomConditionChecker, 
to determine if the annotated service can be created, 
you can also indicate if the service creation must throw an exception in case it fails.
The implementation must have a no-arg constructor, or be an object
Note
The annotation must also be in the framework's classpath.
Example
// Same targets as service annotations
@Target({ElementType.TYPE, ElementType.METHOD})
// The implementation of our CustomConditionChecker
@Condition(type = DevCommandChecker.class)
public @interface DevCommand { }
// Checks services annotated with @DevCommand
public class DevCommandChecker implements CustomConditionChecker<DevCommand> {
    @NotNull
    @Override
    public Class<DevCommand> getAnnotationType() {
        return DevCommand.class;
    }
    @Nullable
    @Override
    public String checkServiceAvailability(@NotNull ServiceContainer serviceContainer, @NotNull Class<?> checkedClass, @NotNull DevCommand annotation) {
        final var config = serviceContainer.getService(Config.class); // Suppose this is your configuration
        if (!config.isDevModeEnabled()) {
            return "Dev mode is disable in the configuration"; // Do not allow the dev commands!
        }
        return null; // No error message, allow the tag command!
    }
}
@Command
@DevCommand // Our custom condition, this command will only exist if it passes.
public class SlashShutdown {
    /* */
}
// Same targets as service annotations
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER)
// The implementation of our CustomConditionChecker
@Condition(type = DevCommandChecker::class)
annotation class DevCommand
// Checks services annotated with @DevCommand
object DevCommandChecker : CustomConditionChecker<DevCommand> {
    override val annotationType: Class<DevCommand> = DevCommand::class.java
    override fun checkServiceAvailability(serviceContainer: ServiceContainer, checkedClass: Class<*>, annotation: DevCommand): String? {
        val config = serviceContainer.getService<Config>() // Suppose this is your configuration
        if (!config.enableDevMode) {
            return "Dev mode is disable in the configuration" // Do not allow the dev commands!
        }
        return null // No error message, allow the tag command!
    }
}
@Command
@DevCommand // Our custom condition, this command will only exist if it passes.
class SlashShutdown {
    /* */
}
Interfaced services¶
Warning
This section only applies to the built-in dependency injection
Interfaced services are interfaces, or abstract class, marked by @InterfacedService,
they must be implemented by a service.
In addition to the service's type, implementations of these annotated interfaces have the interface's type automatically added.
Some interfaced services may only be implemented once, some may allow multiple implementations, if an interfaced service only accepts one implementation, multiple implementations can exist, but only one must be instantiable.
Creating multiple interfaced services in one
You can implement multiple interfaced services at once, which may be useful for text, application and component filters.
Creating interfaced services without registering the interface type
You can also implement an interfaced service, without it being accessible as such, 
by using @IgnoreServiceTypes.
2.X Migration
Most methods in CommandsBuilder accepting interfaces, implementations or lambdas, were moved to interfaced services:
Global:
- CommandsBuilder#setComponentManager: Removed, using components must be enabled in- BComponentsConfigBuilder#enable, and a- ConnectionSupplierservice be present
- CommandsBuilder#setSettingsProvider: Needs to implement- SettingsProvider
- CommandsBuilder#setUncaughtExceptionHandler: Needs to implement- GlobalExceptionHandler
- CommandsBuilder#setDefaultEmbedFunction: Needs to implement- DefaultEmbedSupplierand- DefaultEmbedFooterIconSupplier
Text commands:
- TextCommandBuilder#addTextFilter: Needs to implement- TextCommandFilter, and- TextCommandRejectionHandler
- TextCommandBuilder#setHelpBuilderConsumer: Needs to implement- HelpBuilderConsumer
Application commands:
- ApplicationCommandBuilder#addApplicationFilter: Needs to implement- ApplicationCommandFilter, and- ApplicationCommandRejectionHandler
- ApplicationCommandBuilder#addComponentFilter: Needs to implement- ComponentCommandFilter, and- ComponentCommandRejectionHandler
Extensions:
- ExtensionsBuilder#registerAutocompletionTransformer: Needs to implement- AutocompleteTransformer
- ExtensionsBuilder#registerCommandDependency: Replaced with standard dependency injection
- ExtensionsBuilder#registerConstructorParameter: Replaced with standard dependency injection
- ExtensionsBuilder#registerCustomResolver: Needs to implement- ClassParameterResolverand- ICustomResolver
- ExtensionsBuilder#registerDynamicInstanceSupplier: Needs to implement- DynamicSupplier
- ExtensionsBuilder#registerInstanceSupplier: Replaced by service factories
- ExtensionsBuilder#registerParameterResolver: Needs to implement- ClassParameterResolverand the resolver interface of your choices
Service properties¶
Warning
This section only applies to the built-in dependency injection
Service providers can have names, additional registered types, and an instantiation priority.
Service names¶
Named services may be useful if you have multiple services of the same type, but need to get a specific one.
The name is either defined by using @ServiceName, or with BService#name on the service provider.
Example
You can have a caching HttpClient named cachingHttpClient, while the usual client uses the default name.
Service types¶
In addition to the type of the service provider, 
@ServiceType enables you to register a service as a supertype.
Service priority¶
Service priorities control how service providers are sorted.
A higher priority means that the service will be loaded first, or that an interfaced service will appear first when requesting interfaced services.
The priority is either defined by using @ServicePriority, or with BService#priority on the service provider, 
see their documentation to learn what how service providers are sorted.