Components¶
A component is a plain class that weftkit constructs for you. @Wired marks a class as part of
the graph, and its constructor is the injection point.
Singletons and plain components¶
@Singleton creates a component once during load and injects that instance by type from then on.
A plain @Wired component without @Singleton is created fresh for every injection. Use a
singleton for anything that holds state or does startup work, and a plain component for
throwaway, per-use objects.
@Wired
@Singleton
public final class SpawnerService {
private final Config config;
public SpawnerService(Config config) {
this.config = config;
}
}
Constructor injection¶
Every constructor parameter is resolved from the graph. A parameter can be another component, a
value exposed with @Provides, or an ambient root. The plugin main is an ambient root because it
carries @Registry, so any component can take it.
Binding to interfaces¶
A constructor can depend on an interface or an abstract class. When exactly one @Wired
component implements it, the processor binds the two at compile time and that implementation is
injected. Zero or several implementations fail the build, so the binding is never ambiguous at
runtime.
public interface SpawnerStorage {
void save(Spawner spawner);
}
@Wired
@Singleton
public final class SqlSpawnerStorage implements SpawnerStorage { ... }
@Wired
@Singleton
public final class SpawnerService {
private final SpawnerStorage storage;
public SpawnerService(SpawnerStorage storage) {
this.storage = storage;
}
}
Swapping the SQL implementation for a file based one is a one class change, and tests can
construct SpawnerService with a fake directly since components are plain classes. get also
resolves through bindings, so loader.get(SpawnerStorage.class) returns the bound singleton.
Products with @Provides¶
A public no-argument getter on a singleton, annotated @Provides, exposes its return value to
the graph once the owner has loaded. This is how you inject values you build at runtime rather
than wire by type.
@Wired
@Singleton
public final class Config implements Loader {
private Greeting greeting;
@Override
public boolean load() {
greeting = new Greeting("Hello");
return true;
}
@Provides
public Greeting greeting() {
return greeting;
}
}
Any component can now take a Greeting in its constructor.
Reaching your components¶
BukkitWeft.enable returns a WeftLoader. Use it to reach singletons by type.
For plain components, create builds a fresh instance, and createAll collects every component
assignable to a type. Extra arguments are matched to constructor parameters by type.
Optional dependencies¶
A parameter typed Optional<X> resolves to an empty Optional instead of failing when nothing
provides X, including when a @Provides getter returns null. This is the natural shape for
soft dependencies like a hook into another plugin that may not be installed.
Qualifiers¶
When one type has several implementations or products, @Qualified tells them apart. On a
@Wired class or a @Provides getter it tags what is offered, and on a constructor parameter
it selects the matching tag. The processor checks every qualified dependency at compile time, so
a missing or ambiguous tag fails the build.
@Wired
@Singleton
@Qualified("sql")
public final class SqlStorage implements SpawnerStorage { ... }
@Wired
@Singleton
@Qualified("file")
public final class FileStorage implements SpawnerStorage { ... }
public SpawnerService(@Qualified("sql") SpawnerStorage storage) { ... }
The same works for products, so one singleton can expose two values of the same type.
@Provides
public DataSource main() { ... }
@Provides
@Qualified("archive")
public DataSource archive() { ... }
Lazy singletons¶
@Singleton(lazy = true) defers creation to the first injection instead of building the
component during load. Use it for expensive components that are rarely needed. A lazy singleton
cannot implement Loader or carry @Provides, @Initializes, or @Requires, since it has no
slot in the load order.
Extra ambient roots¶
Arguments to enable after the registry become ambient roots as well, available to every
component by type.