Giter Site home page Giter Site logo

bootique-di's Introduction

build test deploy Maven Central

Bootique is a minimally opinionated java launcher and integration technology. It is intended for building container-less runnable Java applications. With Bootique you can create REST services, webapps, jobs, DB migration tasks, etc. and run them as if they were simple commands. No JavaEE container required! Among other things Bootique is an ideal platform for Java microservices, as it allows you to create a fully-functional app with minimal setup.

Each Bootique app is a collection of modules interacting with each other via dependency injection. This GitHub project provides Bootique core. Bootique team also develops a number of important modules. A full list is available here.

Quick Links

Support

You have two options:

  • Open an issue on GitHub with a label of "help wanted" or "question" (or "bug" if you think you found a bug).
  • Post a question on the Bootique forum.

TL;DR

For the impatient, here is how to get started with Bootique:

  • Declare the official module collection:
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.bootique.bom</groupId>
            <artifactId>bootique-bom</artifactId>
            <version>3.0-M4</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency> 
    </dependencies>
</dependencyManagement>
  • Include the modules that you need:
<dependencies>
    <dependency>
        <groupId>io.bootique.jersey</groupId>
        <artifactId>bootique-jersey</artifactId>
    </dependency>
    <dependency>
        <groupId>io.bootique.logback</groupId>
        <artifactId>bootique-logback</artifactId>
    </dependency>
</dependencies>
  • Write your app:
package com.foo;

import io.bootique.Bootique;

public class Application {
    public static void main(String[] args) {
        Bootique
            .app(args)
            .autoLoadModules()
            .exec()
            .exit();
    }
}

It has main() method, so you can run it!

For a more detailed tutorial proceed to this link.

Upgrading

See the "maven-central" badge above for the current production version of bootique-bom. When upgrading, don't forget to check upgrade notes specific to your version.

bootique-di's People

Contributors

aarrsseni avatar andrus avatar const1993 avatar irus avatar stariy95 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bootique-di's Issues

Duplicate member injection code?

Looking at DefaultBindingBuilder and DICollectionBuilder, I see some duplicated provider wrappers. Seems like we are doing field (and optional method) injection twice everywhere. Removal of the second FieldInjectingProvider / MethodInjectingProvider wrapper doesn't fail any tests either in bootique-di or bootique core. WIthout it, our "di comparison" would look even better :)

diff --git a/bootique-di/src/main/java/io/bootique/di/spi/DICollectionBuilder.java b/bootique-di/src/main/java/io/bootique/di/spi/DICollectionBuilder.java
index 7739e40..b418ee1 100644
--- a/bootique-di/src/main/java/io/bootique/di/spi/DICollectionBuilder.java
+++ b/bootique-di/src/main/java/io/bootique/di/spi/DICollectionBuilder.java
@@ -69,11 +69,8 @@ public abstract class DICollectionBuilder<K, E> implements ScopeBuilder {
         };
 
         Provider<E> provider3 = new CustomProvidersProvider<>(injector, providerType, providerProvider);
-        Provider<E> provider4 = new FieldInjectingProvider<>(provider3, injector);
-        if(injector.isMethodInjectionEnabled()) {
-            provider4 = new MethodInjectingProvider<>(provider4, injector);
-        }
-        return provider4;
+
+        return provider3;
     }
 
     protected <SubT extends E> Provider<SubT> getByTypeProvider(Class<SubT> interfaceType) {
diff --git a/bootique-di/src/main/java/io/bootique/di/spi/DefaultBindingBuilder.java b/bootique-di/src/main/java/io/bootique/di/spi/DefaultBindingBuilder.java
index 43906f6..30fdf30 100644
--- a/bootique-di/src/main/java/io/bootique/di/spi/DefaultBindingBuilder.java
+++ b/bootique-di/src/main/java/io/bootique/di/spi/DefaultBindingBuilder.java
@@ -107,12 +107,7 @@ class DefaultBindingBuilder<T> implements BindingBuilder<T> {
         };
 
         Provider<T> provider3 = new CustomProvidersProvider<>(injector, providerType, providerProvider);
-        Provider<T> provider4 = new FieldInjectingProvider<>(provider3, injector);
-        if(injector.isMethodInjectionEnabled()) {
-            provider4 = new MethodInjectingProvider<>(provider4, injector);
-        }
-
-        addBinding(provider4);
+        addBinding(provider3);
         return this;
     }
 
@@ -127,12 +122,8 @@ class DefaultBindingBuilder<T> implements BindingBuilder<T> {
         @SuppressWarnings("unchecked")
         Class<? extends Provider<? extends T>> providerType = (Class<? extends Provider<? extends T>>)provider1.getClass();
         Provider<T> provider3 = new CustomProvidersProvider<>(injector, providerType, provider1);
-        Provider<T> provider4 = new FieldInjectingProvider<>(provider3, injector);
-        if(injector.isMethodInjectionEnabled()) {
-            provider4 = new MethodInjectingProvider<>(provider4, injector);
-        }
 
-        addBinding(provider4);
+        addBinding(provider3);
         return this;
     }

Module superclass with noop implementation of Module.configure(..)

Module.configure(..) is often empty when @Provides annotations are used. A new BaseModule will serve as a superclass with an empty implementation of this method to simplify custom modules.

Unfortunately we can't provide a default impl in the Module interface itself, as it will stop being a FunctionalInterface as a result, and will not be usable in a lambda form.

Remove List injection

List injection is copied from Cayenne DI, Bootique doesn't use this feature. So it's better to remove it early, to not maintain something we don't use.

Support Method injection

Method injection is part of JSR-330 standard (Guice implements this feature).
Though it is not very useful it is relatively easy to implement and may be handy for some cases.

To keep our speed this feature should be optional.

Improve error message about duplicate values in set

The current message is like this: Found duplicated value 'io.bootique.jetty.MappedFilter@573906eb' in set <BindingKey: java.util.Set[io.bootique.jetty.MappedFilter]>.
It's unclear what exact values are duplicated. We should provide more information about duplicated entries (at least add binding key).

Support for provider methods in modules

Need to support annotated provider methods in modules. E.g.:

public class MyModule implements Module {
   
   @Provides
   MyService provideMyService(MyDep dep) {
        return new MyServiceImpl(dep);
   }
}

Currently public classes with public provider methods will be supported (in contrast with Guice where private/protected/default classes/methods are supported).

Configurable default injection scope

To better align with JSR-330 (#14) we should allow to change default binding scope (currently it's singleton).
Once we allow no scope by default we should process @Singleton annotation.

Optional bindings seem to be order-dependent

I may be stretching the definition of "optional" bindings, but what I am trying to do is to dynamically define a "fallback" binding for a service, with logic for resolving it looking like this:

// for the same Key
if non_optional_binding_present
   return non_optional_binding
else 
   return optional_fallback_binding

However the observed behavior is to treat optional and non-optional bindings as equivalent in regards to the binding definition order (so optional can simply override non-optional when loaded after it).

I tweaked OptionalBindingIT test a bit to show how to reproduce it. The test succeeds or fails depending on the order of modules in the Injector.

// succeeds
Injector injector = DIBootstrap.createInjector(
        b -> {
            b.bindOptional(Service.class).to(Service_Impl2.class);
            b.bind(Consumer1.class);
        },
        b -> b.bind(Service.class).to(Service_Impl1.class)
);

Consumer1 consumer1 = injector.getInstance(Consumer1.class);
assertInstanceOf(Service_Impl1.class, consumer1.service);
// fails
Injector injector = DIBootstrap.createInjector(
        b -> b.bind(Service.class).to(Service_Impl1.class),
        b -> {
            b.bindOptional(Service.class).to(Service_Impl2.class);
            b.bind(Consumer1.class);
        }
);

Consumer1 consumer1 = injector.getInstance(Consumer1.class);
assertInstanceOf(Service_Impl1.class, consumer1.service);

cc: @stariy95

SetBuilder/MapBuilder do not allow binding values of type Class

The new SetBuilder/MapBuilder do not allow binding values of type Class because of ambiguity between add(Class<T>) and add(T) (same for put). We need to disambiguate these method names, following the naming convention in BindingBuilder (i.e. use addInstance / putInstance)

Add methods to use Provider in collection bindings

Only Key<? extends V> valueKey, Class<? extends V> interfaceType or V value can be used in a collection value binding. But there are some case when Provider<? extends V> or Class<? extends Provider<? extends V>> are needed.

Collection elements injection scope

Right now there is no way to control collection element scope.
This can lead to unexpected results.
For example we have an injector with singleton scope by default and want to bind some Map unscoped.
We can try something like this binder.bindeMap(..).withoutScope(). This indeed will return new map instance on each provider call, however content will be always the same as all elements inside map will be bound in default scope (i.e. singleton).

We have two options here:

  • use map scope on all elements (except for elements bound by keys, as we can't control their bindings)
  • allow to define scope for every element in the collection

Add @BQInject annotation

We need to add support for the custom injection point annotation in third-party integrations of Bootique.
Currently, this will solve minor problems with Jersey's HK2 container, that uses same javax.inject.Inject annotation as Bootique DI.

Review error handling

Review DI error handling. Make sure single and multiple errors are reported with good level of details, helping to identify issues.

Perhaps on starting the Injector, we should accumulate multiple binding errors and then throw them all together, to give caller the full picture?

Allow binding of generic types

This is kind of already supported for collections, but IIRC you can't directly bind a specific type like this:

TypeLiteral<A, B> tl = new TypeLiteral<A, B>() {};
binder.bind(tl).toInstance(new MyObject());

This of course should work for all styles of binding, including provider methods per #1 , where this will be implicitly calculated from the method signature.

Unable to use factory method for custom provider

In short, the following code doesn't work:

@Override
public void configure(Binder binder) {
    binder.bind(Service2.class).toProvider(CustomService2Provider.class);
}

@Provides
CustomService2Provider createService2Provider() {
    return new CustomService2Provider("provider");
}

Optional injection

Optional injection can be useful in general.
It can be implemented like this:

Module m = binder -> binder.bindOptional(Service.class);
...
@Inject
Optional<Service> service;
// or
@Inject
Service service; // will be null if binding missing

Support chained bindings

Check and automatically resolve case when service is bound to some interface that has it's own binding.
Here is an example:

binder.bind(Interface.class).to(AnotherInterface.class);
binder.bind(AnotherInterface.class).to(Implementation.class);

This should work for any provider of the AnotherInterface, e.g. for @Provides methods.

See bootique/bootique#282 for the real world case when this is required.

Variable type M can’t be fully resolved

I have abstract Service defenition

  abstract class SomeService<M> {
        @Inject
        protected M field
 }

And some implemetation of that service

SomeServiceImpl extends SomeService<ConcretClass> {
    void someMethod() {
       field.doSomething()
    }
}

I bind that SomeServiceImpl in DI Module just like that:

binder.bind(SomeServiceImpl).withoutScope()
and then I Inject it in any available place

@Inject
SomeServiceImpl service
.........
service.someMethod()
.........


as you can see I pass ConcretClass as M generic type

Error running command ‘--server --config=classpath:application.yml --config=onCourse.yml’
io.bootique.di.DIRuntimeException: Variable type M can’t be fully resolved
at io.bootique.di.TypeLiteral.getArgumentTypes(TypeLiteral.java:235)
at io.bootique.di.TypeLiteral.(TypeLiteral.java:145)
at io.bootique.di.TypeLiteral.of(TypeLiteral.java:55)
at io.bootique.di.spi.FieldInjectingProvider.value(FieldInjectingProvider.java:90)
at io.bootique.di.spi.FieldInjectingProvider.injectMember(FieldInjectingProvider.java:66)
at io.bootique.di.spi.FieldInjectingProvider.injectMembers(FieldInjectingProvider.java:58)
at io.bootique.di.spi.FieldInjectingProvider.injectMembers(FieldInjectingProvider.java:47)
at io.bootique.di.spi.FieldInjectingProvider.injectMembers(FieldInjectingProvider.java:47)
at io.bootique.di.spi.MemberInjectingProvider.get(MemberInjectingProvider.java:53)
at io.bootique.di.spi.TraceableProvider.get(TraceableProvider.java:49)
at io.bootique.di.spi.DefaultScopeProvider.get(DefaultScopeProvider.java:50)
at io.bootique.di.spi.DefaultInjector.getInstanceWithCycleProtection(DefaultInjector.java:268)
at io.bootique.di.spi.DefaultInjector.getInstanceWithCycleProtection(DefaultInjector.java:246)
at io.bootique.di.spi.FieldInjectingProvider.value(FieldInjectingProvider.java:91)
at io.bootique.di.spi.FieldInjectingProvider.injectMember(FieldInjectingProvider.java:66)
at io.bootique.di.spi.FieldInjectingProvider.injectMembers(FieldInjectingProvider.java:58)
at io.bootique.di.spi.MemberInjectingProvider.get(MemberInjectingProvider.java:53)
at io.bootique.di.spi.TraceableProvider.get(TraceableProvider.java:49)
at io.bootique.di.spi.DefaultBindingBuilder.lambda$to$1(DefaultBindingBuilder.java:70)
at io.bootique.di.spi.TraceableProvider.get(TraceableProvider.java:49)
at io.bootique.di.spi.DefaultInjector.getInstanceWithCycleProtection(DefaultInjector.java:268)
at io.bootique.di.spi.DefaultInjector.getInstanceWithCycleProtection(DefaultInjector.java:246)
at io.bootique.di.spi.DefaultInjector.getInstance(DefaultInjector.java:242)
at io.bootique.di.spi.DICollectionBuilder.lambda$getByKeyProvider$2(DICollectionBuilder.java:89)
at io.bootique.di.spi.SetProvider.get(SetProvider.java:49)
at io.bootique.di.spi.SetProvider.get(SetProvider.java:30)
at io.bootique.di.spi.TraceableProvider.get(TraceableProvider.java:49)
at io.bootique.di.spi.DefaultInjector.getInstanceWithCycleProtection(DefaultInjector.java:268)
at io.bootique.di.spi.DefaultInjector.getInstanceWithCycleProtection(DefaultInjector.java:246)
at io.bootique.di.spi.DefaultInjector.getInstance(DefaultInjector.java:242)
at io.bootique.di.spi.ProvidesHandler.lambda$createArgumentProviders$1(ProvidesHandler.java:142)
at io.bootique.di.spi.ProvidesHandler$ProvidesMethodProvider.get(ProvidesHandler.java:174)
at io.bootique.di.spi.TraceableProvider.get(TraceableProvider.java:49)
at io.bootique.di.spi.DefaultInjector.getInstanceWithCycleProtection(DefaultInjector.java:268)
at io.bootique.di.spi.DefaultInjector.getInstanceWithCycleProtection(DefaultInjector.java:246)
at io.bootique.di.spi.DefaultInjector.getInstance(DefaultInjector.java:242)
at io.bootique.di.spi.ProvidesHandler.lambda$createArgumentProviders$1(ProvidesHandler.java:142)
at io.bootique.di.spi.ProvidesHandler$ProvidesMethodProvider.get(ProvidesHandler.java:174)
at io.bootique.di.spi.TraceableProvider.get(TraceableProvider.java:49)
at io.bootique.di.spi.DefaultInjector.getInstanceWithCycleProtection(DefaultInjector.java:268)
at io.bootique.di.spi.DefaultInjector.getInstanceWithCycleProtection(DefaultInjector.java:246)
at io.bootique.di.spi.DefaultInjector.getInstance(DefaultInjector.java:242)
at io.bootique.di.spi.DICollectionBuilder.lambda$getByKeyProvider$2(DICollectionBuilder.java:89)
at io.bootique.di.spi.SetProvider.get(SetProvider.java:49)
at io.bootique.di.spi.SetProvider.get(SetProvider.java:30)
at io.bootique.di.spi.TraceableProvider.get(TraceableProvider.java:49)
at io.bootique.di.spi.DefaultInjector.getInstanceWithCycleProtection(DefaultInjector.java:268)
at io.bootique.di.spi.DefaultInjector.getInstanceWithCycleProtection(DefaultInjector.java:246)
at io.bootique.di.spi.DefaultInjector.getInstance(DefaultInjector.java:242)
at io.bootique.di.spi.ProvidesHandler.lambda$createArgumentProviders$1(ProvidesHandler.java:142)
at io.bootique.di.spi.ProvidesHandler$ProvidesMethodProvider.get(ProvidesHandler.java:174)
at io.bootique.di.spi.TraceableProvider.get(TraceableProvider.java:49)
at io.bootique.di.spi.DefaultScopeProvider.get(DefaultScopeProvider.java:50)
at io.bootique.di.spi.DefaultInjector.getInstanceWithCycleProtection(DefaultInjector.java:268)
at io.bootique.di.spi.DefaultInjector.getInstanceWithCycleProtection(DefaultInjector.java:246)
at io.bootique.di.spi.DefaultInjector.getInstance(DefaultInjector.java:242)
at io.bootique.di.spi.ProvidesHandler.lambda$createArgumentProviders$1(ProvidesHandler.java:142)
at io.bootique.di.spi.ProvidesHandler$ProvidesMethodProvider.get(ProvidesHandler.java:174)
at io.bootique.di.spi.TraceableProvider.get(TraceableProvider.java:49)
at io.bootique.di.spi.DefaultScopeProvider.get(DefaultScopeProvider.java:50)
at ish.oncourse.server.AngelCommand.run(AngelCommand.java:97)
at io.bootique.command.MultiCommand.run(MultiCommand.java:152)
at io.bootique.command.MultiCommand.runBlockingSingle(MultiCommand.java:102)
at io.bootique.command.MultiCommand.runBlocking(MultiCommand.java:94)
at io.bootique.command.MultiCommand.run(MultiCommand.java:73)
at io.bootique.run.DefaultRunner.run(DefaultRunner.java:43)
at io.bootique.BQRuntime.run(BQRuntime.java:81)
at io.bootique.Bootique.exec(Bootique.java:335)
at ish.oncourse.server.AngelServer.main(AngelServer.java:64)

Support for `jakarta.inject` API

Since Bootique 3.0 introduces support for Jakarta APIs in various modules we may think about adding support for jakarta.inject to the Bootique DI

Optional module for Guice compatibility

Create an optional module to ease Guice transition. It doesn't have to work like Guice all the way, but perhaps can emulate Guice annotations, Module and Provider interfaces.

Remove injection of static fields

While playing with #14 found that we injecting into static fields. Think this "feature" should be removed, as it's implemented by accident (just missing check in the code).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.