Skip to content
Roman Khlebnov edited this page Dec 29, 2024 · 2 revisions

If you are here, you are most likely already aware of the dependency injection benefits, so let's get to the point:

  • The main goal is to reduce magic - What You Declare Is What You Get.
  • Additional @Provides annotation for methods to allow creation of module-like functionality in your code.
  • Injector instances are immutable:
    • Builder methods are covered with locks to make them thread-safe (just in case).
  • No additional scopes to JSR 330 (where only @Singleton is defined):
    • Having additional scopes would mean an extension of control over dependencies beyond just injection.
  • Minimalistic API:
    • You can add dependencies.
    • You cannot update or remove dependencies.
    • You can copy Injector to replace dependencies in tests.
  • Handling of both Provider and Supplier functional interfaces in the similar way:
    • The contract of both interfaces is the same, so if you prefer to stick closer to the primitives offered by Java language - with this library you can safely use java.util.function.Supplier instead of jakarta.inject.Provider at the injection points.
  • Handling of the AutoCloseable @Singleton dependencies:
    • Injector itself implements Closeable, allowing the user to choose when dependency resources should be released.
    • Typically, you would want to release resources as the program finishes using something similar to:
Runtime.getRuntime().addShutdownHook(new Thread(injector::close));

Motivation

Have a small, lightweight library which leverages existing standards to wire the code together.

Why not

  • Lack of updates, tests, documentation (and support?).
  • Completely unnecessary feature to inject values into the current class fields.
  • Does not support nested non-static classes.
  • Complicated API.
  • Code generation while being the fastest approach requires code rebuild every time.
  • Too many annotations.
  • Still certain hidden magic.
  • Misleading Key API, especially when in comes to some of the Qualifier annotations like @Named.

Any other?

  • The need for a specific, small library which does one specific thing—nothing more.

Internal representation of dependencies

A map of key to graph-like nodes, which contain references to their parent keys.

Overall map representation looks similar to graph adjacency list representation.

Features you can reuse

  • io.github.suppierk.utils.Memoized class, which represents lazy value that gets evaluated once:
    • While there are many implementations of the same logic, the name Memoized aligns with what frontend teams typically use to denote similar behavior on their side.
    • Unlike typical double-checked locking implementations, Memoized uses locks making it less prone for thread pinning issue in Java 21 and virtual threads.
    • Implements and can be constructed from both java.util.function.Supplier and jakarta.inject.Provider.