-
Notifications
You must be signed in to change notification settings - Fork 1
How to use the Dependency Injection
SimplixCore utilizes enterprise-level development techniques powered by Google to ensure fast development by reducing the need for boilerplate code. If you want to learn more about the used Dependency Injection framework Guice, you can check out the wiki.
According to Wikipedia:
In software engineering, dependency injection is a technique in which an object receives other objects that it depends on. These other objects are called dependencies. In the typical "using" relationship[1] the receiving object is called a client and the passed (that is, "injected") object is called a service. The code that passes the service to the client can be many kinds of things and is called the injector. Instead of the client specifying which service it will use, the injector tells the client what service to use. The "injection" refers to the passing of a dependency (a service) into the object (a client) that would use it.
In SimplixCore, we have so called components. Components can serve as client and service at the same time. This means that every component can require other components as dependencies.
This image shows the Dependency Injection model of a SimplixApplication. Continue reading with this image in mind.
The injector is the heart of Dependency Injection. It holds the information about every configured module and its components. The injector is used to inject constructor parameters or get instances from configured bindings. Every application has its own injector.
Modules are used to divide components from specific aspects from each other. For example, having a BungeeCordModule
and a SpigotModule
ensures that components bound to the SpigotModule
are only usable in contexts where the SpigotModule
is available.
You can configure own bindings to a module. A binding describes the relation between a Key and its Provider. See https://github.com/google/guice/wiki/Bindings for further information on that.
The class AbstractSimplixModule
serves some additional AOP features like intercepting detected Component
s. You can register a ComponentInterceptor
during the initialization of the module:
@Slf4j
@ApplicationModule("Example")
public final class ExampleModule extends AbstractSimplixModule {
{
registerComponentInterceptor(Listener.class, listener -> {
log.info("A wild listener is passing by: " + listener.getClass().getName());
// Do listener registration and stuff
});
}
}
This interceptor will print every subclass of Listener
that was annotated with @Component
and was bound to ExampleModule
into the log.
Annotations are used for further reducing boilerplate code.
Any class that was marked with this annotation is a component. It can require other dependencies and or can be required from other dependencies. If this class requires other dependencies, the constructor must be annotated with @Inject
. For example:
@Component(ExampleModule.class)
public final class CarCacheHandler {
private final CarSqlHandler carSqlHandler;
private final LoadingCache<Integer, Car> carCache =
CacheBuilder.newBuilder().build(new CacheLoader<Integer, Car>() {
@Override
public Car load(@NotNull Integer integer) {
return carSqlHandler.loadById(integer);
}
});
@Inject
public CarCacheHandler(CarSqlHandler carSqlHandler) {
this.carSqlHandler = carSqlHandler;
}
public Car getCar(int id) {
return carCache.getUnchecked(id);
}
}
This class requires an instance of CarSqlHandler
to be constructed. The class CarSqlHandler
is also annoted with @Component
so it can be injected by the injector. Other components can now require this class to get some cars from the database, in this example.
Normally, Component
s are lazy constructed. When there is no need for a specific component to be injected, there will also be no instance of this component. A component annotated with @AlwaysConstruct
will always be constructed during application installation.
Notice: ComponentInterceptor
s will also construct their intercepted components.
Only usable in combination with @SimplixApplication
. This class makes it possible to create module instances using a specified Supplier<AbstractSimplixModule[]>
. This is necessary when your AbstractSimplixModule
extension requires constructor parameters which would make the automatic registration using @ApplicationModule
impossible.
Only usable in combination with @SimplixApplication
. This advises the SimplixInstaller
to look at specified locations for components and modules. Typically this points to the common base package of your application. For example: @ScanComponents("dev.simplix.core")
This marks a class for automatic Module
detection during module scanning. The annotated module needs to have an accessible default constructor. Otherwise please register the module using @RequireModules
or using the register
method of SimplixInstaller
.
SimplixApplications can depend on each other. An application called A
which is depending on an application called B
can access every binding of application B
. In some cases (e.g. localization) you won't want to let other applications access some bindings of your application. In that case, you can annotate that binding with @Private
.
When you want to utilize the features of dependency injection, go for it. But don't construct components using their constructor. This will result in a new instance which is different from the one Guice is using for other components. Use something like that instead:
CarSqlHandler sqlHandler = SimplixInstaller.instance().injector(MyApplication.class).getInstance(CarSqlHandler.class);
And this isn't beautiful nor conventional. Better you design your application with DI in mind. Your goal to achieve is: Everything is a component.
2020 - SimplixSoftworks
- Introduction
- Getting started
- Maven Dependencies
- How to use the Dependency-Injection
- Localization
- Listener API
- SQL
- Libraries
- Utilities
- Contribute