Simple, JSR330-compatible Dependency Injection.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Paweł Płazieński 37acfe73c0 Remove discouraged non-functional factory registrations 3 months ago
src Remove discouraged non-functional factory registrations 3 months ago
.gitignore Restore gitignore 3 years ago
LICENSE.md Add GPL license 3 years ago
README.md Add functional style provider registration 2 years ago
pom.xml Allow providing external qualifiers for non-reference factories 3 months ago

README.md

Injectable

Simple, JSR330-compatible Dependency Injection.

Introduction

Injectable is a extremely simple and small Dependency Injection library that offers:

  • Construction by type or Provider,
  • Providing externally constructed singletons,
  • Adding external dependency fetching mechanism,
  • Custom scopes,
  • Injected instance replacement,
  • Custom object queries.

Usage

Entry point for both configuration and usage is StandardRegistry, in which dependencies are registered and instances are constructed. This class implements two interfaces, Registry and Configuration.

Registration

Standard

StandardRegistry registry = StandardRegistry.create();

In registry, you can register a previously-created singleton instance:

registry.registerSingleton(new MyCustomBuiltService());

a type constructed by @Inject annotated constructor (or default one):

registry.registerType(MyStandardInjectedService.class);

Type constructed by provider:

registry.registerProvider(MyProvidedService.class, MyProvidedService::createCustom);

Also just by providing serializable functional interface:

registry.registerProvider(MyProvidedService::createCustom);

Or external dependency fetching mechanism:

registry.registerExternal((targetClass, annotationMatch) -> 
    networkResource.retrieveClassMatching(targetClass).matching(annotationMatch));

Or built from factory, where factory instance is constructed by registry:

registry.registerFactory(MyFactoryService.class, MyProductService.class, MyFactoryService::create);

Also just by providing serializable functional interface:

registry.registerFactory(MyFactoryService::create);

You can also write your own implementation of Registration or Construction and register it in registry:

registry.register(myVerySpecialRegistration);

Additionally qualified

You can mark any registration with additional qualifiers or scopes:

registry.register(Registration.type(MyDistinctService.class).with(Distinct.class));

(where Distinct is annotation meta-annotated either by javax.inject.Scope or javax.inject.Qualifier)

To quickly add javax.inject.Named qualifier, use:

registry.register(Registration.type(MyExquisiteService.class).withName("exquisite"));

Configuration

Scopes

Prototype and singleton scopes are registered by default, so not using any scope annotation will create new instance each fetch request, and using javax.inject.Singleton will register single instance within StandardRegistry.

If you like to annotate every class with scope, you can use org.perfectable.injection.scope.Prototype.

To create custom scope, create an scope-annotated annotation:

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ThreadScoped {
	// marker
}

Implement a scope:

public class ThreadScope implements Scope {
    @Override
    public <T> Storage<T> requestStorage() {
        ThreadLocal<T> instanceStorage = new ThreadLocal<>();
        return (createdClass, provider) -> {
            T instance = instanceStorage.get();
            if (instance == null) {
                instance = provider.get();
                instanceStorage.set(instance);
            }
            return instance;
        };
    }
}

And register it in registry.

registry.addScope(ThreadScoped.class, threadScope);

Every object registered with this scope annotation will be placed in specified scope. and extracted from there.

Replacement

Instances can be replaced before injecting as dependency, for example with proxy:

registry.addReplacement((createdClass, provider) -> 
    proxyFactory.create(createdClass).initializedBy(provider));

Extraction

To obtain instance from registry create query for instances:

MyStandardInjectedService service = 
    registry.fetchAny(Query.typed(MyStandardInjectedService.class));

To request existing qualifiers use:

MyDistinctService service = 
    registry.fetchAny(Query.typed(MyDistinctService.class).qualifiedWith(Distinct.class));

To request instance annotated with special javax.inject.Named:

MyExquisiteService service = 
    registry.fetchAny(Query.typed(MyExquisiteService.class).named("exquisite"));

These can be chained:

MyProvidedService service = 
    registry.fetchAny(Query.typed(MyProvidedService.class).named("unbugged").qualifiedWith(Fast.class));

Quick reference

@Singleton
public class Application {
    @Inject
    @Transactional
    private DatabaseConnection databaseConnection;
    
    @Inject
    @Named("credit")
    private PaymentService paymentService;
    
    public static void main(String[] args) {
        DatabaseConnection databaseConnection = DatabaseConnection.create(args);
        StandardRegistry registry = StandardRegistry.create();
        registry.registerType(CreditPaymentService.class);
        registry.registerType(StandardAuthenticationService.class); // required for CreditPaymentService
        registry.register(Registration.singleton(databaseConnection).with(Transactional.class));
        registry.registerType(Application.class);
        Application configuredApplication = registry.fetchAny(Query.typed(Application.class));
        configuredApplication.execute();
    }

    private void execute() {
        // ...
    }
}

If you'd also use introspectable, you can register every annotated class with your custom annotation:


public static void main(String[] args) {
    StandardRegistry registry = StandardRegistry.create();
    ClassQuery.all()
        .inPackage(Application.class.getPackage()) // Or any other prefix
        .annotatedWith(Component.class)
        .forEach(registry::registerType);
    Application configuredApplication = registry.fetchAny(Query.typed(Application.class));
    configuredApplication.execute();
}

Usage in maven

Add as dependency:

<dependency>
    <groupId>org.perfectable</groupId>
    <artifactId>injectable</artifactId>
    <version>2.0.1-SNAPSHOT</version>
</dependency>

Currently, injectable artifacts is stored on perfectable.org maven repository only, so you need to add following entry to your repositories:

<repository>
    <id>perfectable-all</id>
    <name>Perfectable</name>
    <url>https://maven.perfectable.org/repo</url>
</repository>