The core plugin provides the IocContainer for Tubing framework.
This dependency is always needed when developing a Tubing plugin
Beans
A Tubing bean is an instance of a class registered inside the Tubing IOC container.
A bean can be registered in 2 ways.
Annotating the class with a bean annotation. The core plugin only provides @IocBean.
But other dependencies can declare there own annotations to instantiate beans.
This can be seen, for example, in the tubing-bukkit extension.
Providing the bean through a class and provider method
A Tubing bean can only have one constructor! The parameters of the constructor can only be other Tubing beans or configuration properties injected with an @ConfigProperty annotation
Tubing can inject beans in 3 ways:
Inject a concrete class instance
Inject an interface with exactly one implementation
Inject a List of interface implementations.
Injection
Constructor injection
To inject properties we need to provide all needed beans inside the constructor of the class.
Tubing will search for the dependencies and inject them.
@IocBean
public class ReportService {
private final PermissionHandler permission;
private final ReportRepository reportRepository;
public ReportService(PermissionHandler permission, ReportRepository reportRepository) {
this.permission = permission;
this.reportRepository= reportRepository;
}
}
Interface injection
List injection
An example of this could be (this is not a good example):
@IocBean
@IocMultiProvider(Listener.class)
public class PlayerJoinListener implements Listener {
}
@IocBean
@IocMultiProvider(Listener.class)
public class AsyncPlayerChatListener implements Listener {
}
@IocBean
public class AllListenersExample {
private final List<Listener> listeners;
public AllListenersExample(@IocMulti(Listener.class) List<Listener> listeners){
this.listeners = listeners;
}
}
Example
ReportService bean
The ReportService is an example of a concrete class that is registered as a Tubing bean
import be.garagepoort.mcioc.IocBean;
@IocBean
public class ReportService {
private final PermissionHandler permission;
private final ReportRepository reportRepository;
public ReportService(PermissionHandler permission, ReportRepository reportRepository) {
this.permission = permission;
this.reportRepository= reportRepository;
}
...
}
ReportRepository bean
The ReportRepository is an interface that will have at runtime one bean registered inside the IOC container.
public interface ReportRepository {
int addReport(Report report);
}
Mysql implementation
Notice the conditionalOnProperty . This means this specific implementation will only be instantiated and registered if the storage.type inside the config.yml is set to mysql.
@IocBean(conditionalOnProperty = "storage.type=mysql")
public class MysqlReportRepository implements ReportRepository {
@Override
public int addReport(Report report) {
...
}
}
Sqlite implementation
Notice the conditionalOnProperty . This means this specific implementation will only be instantiated and registered if the storage.type inside the config.yml is set to sqlite.
@IocBean(conditionalOnProperty = "storage.type=sqlite")
public class SqliteReportRepository implements ReportRepository {
@Override
public int addReport(Report report) {
...
}
}
PermissionHandler bean
The permission handler in this example is an interface that will have at runtime one bean registered.
public interface PermissionHandler {
boolean has(Player player, String permission);
}
The below implementations are not annotated with the @IocBean annotation. This is because they are registered with a provider method.
DefaultPermissionHandler
public class DefaultPermissionHandler implements PermissionHandler {
private Options options;
public DefaultPermissionHandler(Options options) {
this.options = options;
}
public boolean has(Player player, String permission) {
...
}
}
GroupManagerPermissionHandler
public class GroupManagerPermissionHandler implements PermissionHandler {
private final Options options;
private final GroupManager gMplugin;
public GroupManagerPermissionHandler(Options options) {
this.options = options;
...
}
public boolean has(Player player, String permission) {
...
}
}
VaultPermissionHandler
public class VaultPermissionHandler implements PermissionHandler {
private static Permission perms = null;
private final Options options;
public VaultPermissionHandler(Options options) {
this.options = options;
...
}
public boolean has(Player player, String permission) {
...
}
}
Provider method
The below method decides which bean to register at runtime. This is another way to conditionally choose which interface implementation to register at runtime.
The provider method needs to be static!
@TubingConfiguration
public class TubingConfiguration {
@IocBeanProvider
public static PermissionHandler instantiatePermissionHandler(Options options) {
final PluginManager pluginManager = TubingExample.get().getServer().getPluginManager();
Plugin gMplugin = pluginManager.getPlugin("GroupManager");
if(gMplugin != null && gMplugin.isEnabled()) {
TubingExample.get().getLogger().info("GroupManager found. Permissions will be handled by GroupManager");
return new GroupManagerPermissionHandler(options);
}
RegisteredServiceProvider<Permission> registration = Bukkit.getServer().getServicesManager().getRegistration(Permission.class);
if (registration != null) {
TubingExample.get().getLogger().info("Vault found. Permissions will be handled by Vault");
return new VaultPermissionHandler(options);
}
TubingExample.get().getLogger().info("Permissions handled by Bukkit");
return new DefaultPermissionHandler(options);
}
}
If you have an interface with one implementation registered at runtime, Tubing will inject this one implementation. If you have multiple implementation of one interface at runtime Tubing will throw an exception indicating it can not inject the dependency as it does not know which to Inject. The permission handler is a good example of this.
If you want multiple implementations of an interface you need a and use List Injection.
You can inject a list of classes which all implement the same interface. To do so you need to annotate the bean with and when injection you need to use the annotation.