1. Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.
    Скрыть объявление
Скрыть объявление
В преддверии глобального обновления, мы проводим исследования, которые помогут нам сделать опыт пользования форумом ещё удобнее. Помогите нам, примите участие!

Стартап Туториал | Внедрение зависимостей в плагинах через Dagger 2

Тема в разделе "Разработка плагинов для новичков", создана пользователем SashaBrine, 31 май 2024.

  1. Автор темы
    SashaBrine

    SashaBrine Активный участник Пользователь

    Баллы:
    76
    Имя в Minecraft:
    IJustFortiLive
    Dagger 2 - это инструмент, предоставляющий возможность внедрения зависимостей в Java/Kotlin-приложения. Внедрение зависимостей (Dependency Injection, DI) - это шаблон проектирования, позволяющий уменьшить связность между объектами и упростить тестирование кода.

    Начнем с базовой части. Создадим наш проект с таким билд-файлом:
    Код:
    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 считается самой производительной.
     
  2. Dymeth

    Dymeth Активный участник Пользователь

    Баллы:
    98
    Имя в Minecraft:
    Dymeth

Поделиться этой страницей