Skip to content

Dependency Injection

Christopher Bishop edited this page Jan 17, 2017 · 12 revisions

DEVIN has it's own flexible dependency injection system for the commands, events and pretty much anything you could possibly need. This was made to save more time and make the code more maintainable by eliminating constructors that would just contain the plugin and a few other things across multiple objects with the same values. We could make the variables static, but if you injected them we could keep everything private and out of reach of other plugins. Before we get into injecting our managers, plugins, and other stuff into our command classes and events, lets talk about the basics.

Injector

Everything that goes into commands, events, and other objects comes from the injector. Each plugin is assigned it's own global injector which can be obtained like so:

public class MyPlugin extends JavaPlugin {

    @Override
    public void onEnable() {
        Injector injector = Devin.getInjector(this); // Global Injector
        Injector localInjector = new Injector(); // Local Injector.
    }

}

Adding Injections

Injections are the objects that are going to be injected into your objects. You can either add one with a name or without one. If the injection has a name, the name of the field in every object must match that name. This is useful for when you need multiple objects of the same type, since each injection needs a unique name and type pair. If you do not include a name, then the fields can be named whatever you want them to be.

public class MyPlugin extends JavaPlugin {

    private SomeManager manager;
    private ThingManager thingManager;

    @Override
    public void onEnable() {
        manager = new SomeManager();
        thingManager = new ThingManager();
        
        Injector injector = Devin.getInjector(this); // Global Injector
        injector.add(manager); // Injections without a name
        injector.add(thingManager, "thingManager"); // Named injection
    }

}

Injecting Objects

Lets get to the part where we can finally see some results, injecting our object. To have a field accept our injection, we must annotate it with @Inject. The field must match at least one of the injections that we gave it or it will through an error while debugging. This will not break the code, the variable will just be the default. Lets make a simple class that can accept our injections.

public class MyObject {

    @Inject
    private SomeManager manager;

    @Inject
    private ThingManager thingManager;

    public SomeManager getManager() { return manager; }
    public ThingManager getThingManager() { return thingManager; }

}

Now we have a class that can accept our injections, so lets inject it!

public class MyPlugin extends JavaPlugin {

    private SomeManager manager;

    @Override
    public void onEnable() {
        manager = new SomeManager();
        
        Injector injector = Devin.getInjector(this); // Global Injector
        injector.add(manager); // Injections without a name

        MyObject obj = new MyObject();
        injector.inject(obj);

        System.out.println(obj.getManager());
    }

}

Local Injections

The injector also allows you to pass in local injections so that one object specifically gets additional or different injections. Any local injections that have equivalent name and type pair in the global injections would override the global injections entirely. To do this, you need to pass in InjectedObject's into the inject method. An InjectedObject is a object which contains the name and type information, as well as the real value. These are automatically created behind the scenes when using the add methods.

public class MyPlugin extends JavaPlugin {

    private SomeManager manager;

    @Override
    public void onEnable() {
        manager = new SomeManager();
        
        Injector injector = Devin.getInjector(this); // Global Injector
        injector.add(manager); // Injections without a name

        MyObject obj = new MyObject();
        injector.inject(obj, new InjectedObject(new SomeManager("different")));

        System.out.println(obj.getManager());
    }

}

Injecting Commands

Now we get to the fun stuff, injecting our commands. If you haven't read through Command 101, then I suggest you do so because this will contain information that was gone over on that page. Injecting into commands is very similar to how we injected objects.

public class MyPlugin extends JavaPlugin {

    private SomeManager manager;

    @Override
    public void onEnable() {
        manager = new SomeManager();
        
        Injector injector = Devin.getInjector(this); // Global Injector
        injector.add(manager); // Injections without a name

        CommandRegistrar cr = new CommandRegistrar(this, new MessageSender());
        cr.registerCommands(new MyCommands()); // Registers and injects command.
    }

}

When injecting into a command, there is no need to call the inject method. Just add your injections as you would above and register your command. The one exception is that the MessageSender is injected as well without having to add it.

Injecting Event Listeners

DEVIN has a short cut way of injecting your event listeners without having to inject the object then register the listener.

public class MyPlugin extends JavaPlugin {

    private SomeManager manager;

    @Override
    public void onEnable() {
        manager = new SomeManager();
        
        Injector injector = Devin.getInjector(this); // Global Injector
        injector.add(manager); // Injections without a name

        Devin.registerEvents(new MyListener(), this);
    }

}

This method will now inject your events and register your event listener at the same time. This also shortens the code a few characters from Bukkit.getPluginManager().registerEvents.

Clone this wiki locally