- Баллы
- 76
Dagger 2 - это инструмент, предоставляющий возможность внедрения зависимостей в Java/Kotlin-приложения. Внедрение зависимостей (Dependency Injection, DI) - это шаблон проектирования, позволяющий уменьшить связность между объектами и упростить тестирование кода.
Начнем с базовой части. Создадим наш проект с таким билд-файлом:
Далее мы реализуем код для выполнения внедрения. Более конкретно, мы создадим:
Итак, создадим наш компонент. Я создаю как правило структуру проекта в таком виде - com.example.plugin.di.component. Создадим там наш компонент.
Сам Dagger имеет механизм обработки аннотаций и генерации кода. Поэтому он с нашего интерфейса сгенерирует DaggerPluginComponent котоырй мы будем использовать в нашем главном классе:
Теперь хочу вам показать внедрение зависимостей на примере слушателей евентов. Я предлагаю создавать класс Listeners в котормо и будут наши слушатели. Вот пример создания:
Dagger сам достаёт экземпляр класса DaggerSamplePlugin и SampleListener и даёт нам.
Dagger передаёт нашему классу экземпляр класса DaggerSamplePlugin
Ещё хочу сказать что нужно внедрить Listeners в наш плагин, иначе Dagger не сможет понять что нужно инжектить класс Listeners. Dagger не Spring, он работаёт по цепочке.
Давайте внедрим ещё какую-то зависимость в наш слушатель. Создадим класс BanManager например:
Внедрим в наш слушатель:
Dagger создаст нашу утилиту и передаст слушателю.
Теперь нужно понять как работают модули и как их использовать. Создадим самый простой модуль
Нужно нашему компоненту сказать что мы хотим использовать наш модуль:
Объясняю: в связи с тем, что у Server нет ни конструктора с аннотацией @Inject, ни провайдера, при попытке внедрить зависимость возникнет ошибка. Мы создали метод-провайдер, и теперь ошибки не будет.
Сам туториал я создал, потому что в интернете нет гайдов по работе с Spigot и Dagger. Сейчас существуют и другие DI-библиотеки, такие как Google Guice или Spring Framework. Dagger считается самой производительной.
Начнем с базовой части. Создадим наш проект с таким билд-файлом:
Код:
plugins {
java
}
group = "me.fortibrine"
version = "1.0"
repositories {
mavenCentral()
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
maven("https://oss.sonatype.org/content/repositories/central")
}
dependencies {
compileOnly("org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT")
implementation("com.google.dagger:dagger:2.47")
annotationProcessor("com.google.dagger:dagger-compiler:2.47")
}
tasks {
withType<JavaCompile>().configureEach { options.encoding = "UTF-8" }
jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from (
configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) }
)
}
}
Далее мы реализуем код для выполнения внедрения. Более конкретно, мы создадим:
- модуль, который представляет собой класс, предоставляющий или создающий зависимости объектов, и
- компонент, который представляет собой интерфейс, используемый для генерации инжектора.
Итак, создадим наш компонент. Я создаю как правило структуру проекта в таком виде - com.example.plugin.di.component. Создадим там наш компонент.
Код:
package me.fortibrine.daggersample.di.component;
import dagger.BindsInstance;
import dagger.Component;
import me.fortibrine.daggersample.DaggerSamplePlugin;
import javax.inject.Singleton;
@Singleton
@Component
public interface PluginComponent {
public void inject(DaggerSamplePlugin plugin);
@Component.Factory
public interface Factory {
public PluginComponent create(@BindsInstance DaggerSamplePlugin plugin);
}
}
Сам Dagger имеет механизм обработки аннотаций и генерации кода. Поэтому он с нашего интерфейса сгенерирует DaggerPluginComponent котоырй мы будем использовать в нашем главном классе:
Код:
package me.fortibrine.daggersample;
import me.fortibrine.daggersample.di.component.DaggerPluginComponent;
import org.bukkit.plugin.java.JavaPlugin;
public class DaggerSamplePlugin extends JavaPlugin {
public void onEnable() {
DaggerPluginComponent.factory()
.create(this)
.inject(this);
}
}
Теперь хочу вам показать внедрение зависимостей на примере слушателей евентов. Я предлагаю создавать класс Listeners в котормо и будут наши слушатели. Вот пример создания:
Код:
package me.fortibrine.daggersample.listener;
import me.fortibrine.daggersample.DaggerSamplePlugin;
import org.bukkit.Bukkit;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class Listeners {
@Inject
public Listeners(DaggerSamplePlugin plugin, SampleListener sampleListener) {
Bukkit.getPluginManager().registerEvents(sampleListener, plugin);
}
}
Dagger сам достаёт экземпляр класса DaggerSamplePlugin и SampleListener и даёт нам.
Код:
package me.fortibrine.daggersample.listener;
import me.fortibrine.daggersample.DaggerSamplePlugin;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class SampleListener implements Listener {
private final DaggerSamplePlugin plugin;
@Inject
public SampleListener(DaggerSamplePlugin plugin) {
this.plugin = plugin;
plugin.getLogger().info("SampleListener registered");
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
event.setJoinMessage(null);
plugin.getServer().broadcastMessage(event.getPlayer().getName() + " joined!");
}
}
Dagger передаёт нашему классу экземпляр класса DaggerSamplePlugin
Ещё хочу сказать что нужно внедрить Listeners в наш плагин, иначе Dagger не сможет понять что нужно инжектить класс Listeners. Dagger не Spring, он работаёт по цепочке.
Код:
package me.fortibrine.daggersample;
import me.fortibrine.daggersample.di.component.DaggerPluginComponent;
import me.fortibrine.daggersample.listener.Listeners;
import org.bukkit.plugin.java.JavaPlugin;
import javax.inject.Inject;
public class DaggerSamplePlugin extends JavaPlugin {
@Inject
Listeners listeners;
public void onEnable() {
DaggerPluginComponent.factory()
.create(this)
.inject(this);
}
}
Давайте внедрим ещё какую-то зависимость в наш слушатель. Создадим класс BanManager например:
Код:
package me.fortibrine.daggersample.utils;
import me.fortibrine.daggersample.DaggerSamplePlugin;
import org.bukkit.BanList;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class BanManager {
private DaggerSamplePlugin plugin;
@Inject
public BanManager(DaggerSamplePlugin plugin) {
this.plugin = plugin;
}
public void banPlayer(Player player) {
plugin.getServer().getBanList(BanList.Type.NAME).addBan(player.getName(), "Rules", null, "Console");
}
public void unbanPlayer(Player player) {
plugin.getServer().getBanList(BanList.Type.NAME).pardon(player.getName());
}
}
Внедрим в наш слушатель:
Код:
package me.fortibrine.daggersample.listener;
import me.fortibrine.daggersample.DaggerSamplePlugin;
import me.fortibrine.daggersample.utils.BanManager;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class SampleListener implements Listener {
private final DaggerSamplePlugin plugin;
private final BanManager banManager;
@Inject
public SampleListener(DaggerSamplePlugin plugin, BanManager banManager) {
this.plugin = plugin;
this.banManager = banManager;
plugin.getLogger().info("SampleListener registered");
}
@EventHandler
public void onKill(PlayerDeathEvent event) {
if (event.getEntity().getKiller() != null) {
banManager.banPlayer(event.getEntity().getKiller());
}
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
event.setJoinMessage(null);
plugin.getServer().broadcastMessage(event.getPlayer().getName() + " joined!");
}
}
Dagger создаст нашу утилиту и передаст слушателю.
Теперь нужно понять как работают модули и как их использовать. Создадим самый простой модуль
Код:
package me.fortibrine.daggersample.di.module;
import dagger.Module;
import dagger.Provides;
import me.fortibrine.daggersample.DaggerSamplePlugin;
import org.bukkit.Server;
import javax.inject.Singleton;
@Module
public class SampleModule {
@Singleton
@Provides
public Server provideServer(DaggerSamplePlugin plugin) {
return plugin.getServer();
}
}
Код:
package me.fortibrine.daggersample.di.component;
import dagger.BindsInstance;
import dagger.Component;
import me.fortibrine.daggersample.DaggerSamplePlugin;
import me.fortibrine.daggersample.di.module.SampleModule;
import javax.inject.Singleton;
@Singleton
@Component(modules = {SampleModule.class})
public interface PluginComponent {
public void inject(DaggerSamplePlugin plugin);
@Component.Factory
public interface Factory {
public PluginComponent create(@BindsInstance DaggerSamplePlugin plugin);
}
}
Объясняю: в связи с тем, что у Server нет ни конструктора с аннотацией @Inject, ни провайдера, при попытке внедрить зависимость возникнет ошибка. Мы создали метод-провайдер, и теперь ошибки не будет.
Сам туториал я создал, потому что в интернете нет гайдов по работе с Spigot и Dagger. Сейчас существуют и другие DI-библиотеки, такие как Google Guice или Spring Framework. Dagger считается самой производительной.