Minimalistic, annotation-based Java library for dependency injection. Compatible with the traditional JVM, Android, GraalVM, etc.
- Constructor-based dependency injection
- Singleton, and prototype DI scopes
- Bean akin components which can register, or be injected with components
- Ability to do classpath scanning of the eligible for DI components
- Support for programmatic registration of additional components
- Runtime or build time component scanning, allowing DI on platforms with limited reflection capabilities (Android, GraalVM native images, etc.)
- Simple to learn and use
package tinydi.helloworld;
import com.github.zhgzhg.tinydi.TinyDI;
import com.github.zhgzhg.tinydi.components.*;
import com.github.zhgzhg.tinydi.meta.annotations.*;
import com.github.zhgzhg.tinydi.meta.enums.ScopeDI;
import java.util.Date;
/** Component responsible for the initialization of other core for the app components. */
@Registrar
class Config {
/** A singleton component called 'appVersion' which is a string */
@Recorded
String appVersion() { return "1.0.0"; }
/** A prototype component called 'currentTime' */
@Recorded(value = "currentTime", scope = ScopeDI.PROTOTYPE)
Date currDate() { return new Date(); }
}
/** Interface contract for printing an app's version and current date */
interface Informer {
void printInfo();
}
/** A component which will be injected with the 'Recorded' component instances or possibly other 'Supervised' ones. */
@Supervised
class AppInfo implements Informer {
private String version;
private Date currDt;
/**
* Constructor which will be called automatically by TinyDI. If several eligible for injection
* parameters of the same data type exist the @KnownAs annotation can be used to specify a concrete one.
* @param appVersion Will be injected, taken from the Config class.
* @param ct Will be injected, taken from the Config class. The KnownAs annotation is not mandatory in
* this example, but it's there for demonstration purposes.
*/
public AppInfo(String appVersion, @KnownAs("currentTime") Date ct) {
this.version = appVersion;
this.currDt = ct;
}
@Override
public void printInfo() {
System.out.printf("App (%s) - v%s%n", this.currDt, version);
}
}
/** The entrypoint of the Java app, and also in this example of the dependency injected app too. */
@Supervised
public class Main implements EntryPoint {
public static void main(String[] args) {
TinyDI.config()
.basePackages(Main.class.getPackageName())
.configure()
.run();
}
private Informer informer;
public Main(Informer informer) {
this.informer = informer;
}
/** The entry point of the DI-ed app when everything is initialised */
@Override
public void run() {
this.informer.printInfo();
}
}
- Only dependency injection via constructor is supported
- You may use Lombok to reduce the boilerplate code
- Primitive types are always converted to their wrappers (for e.g. int -> Integer, long -> Long, etc...)
- Nulls are not considered valid dependency injection values and will be ignored by the library
- Android and GraalVM native images require serialization of the component scanning to be saved at build time
Consult with the wiki and the API documentation for more information.