I have the following class :
class ClassWithInjectedConstructor #Inject constructor(private val simpleInjectedObject: SimpleInjectedObject) : ClassWhichThisInheritsFrom() {
override fun check(): int {
val result = simpleInjectedObject.methodToCall() // Returns an int.
return 1;
}
}
Please ignore that this function doesn't do anything except return the value on 1.
I am trying to get a handle on how the hell I work with this class, and it's injected constructor.
In my main application class (This class is java, not kotlin), I need to use the class above... and this is how I am trying to do it :
final ClassWithInjectedConstructor instance = new ClassWithInjectedInstructor();
I am aware that I need to pass something into that constructor, but how? If it is injected, do I need some fancy syntax?
Constructor injection with Dagger means that Dagger will create the object by calling the constructor and pass all the dependencies in for you. So when you call new Something() you're effectively not making use of constructor injection. You're simply creating an object.
All you really have to do is add the #Inject annotation on the constructor. That way Dagger knows about your class and if all of its dependencies can be provided Dagger can also provide that class. That's really all you need to do and when you want to use the class somewhere else you just have to request it.
If using field injection (e.g. in your Activity) you can just add an annotated field and Dagger will inject it along with all the other dependencies
#Inject lateinit var something: Something
fun onCreate(..) { activityComponent.inject(this) }
If using it in yet another class, you can just add it to the constructor...using constructor injection again...
class OtherThing #Inject constructor(val something : Something)
Or add a provision method to your component and "request" it later...
#Component interface MyComponent {
fun provideSomething : Something
}
// ...
val something : Something = myComponent.provideSomething()
If in your example SimpleInjectedObject can not be provided by Dagger, e.g. it does not use constructor injection and you did not add a #Provides annotated method that can provide it to any of your modules, you will get a build error stating that SimpleInjectedObject cannot be provided... about which you can find more information here.
Related
Basically, I want to make this code work:
#Component
abstract class MyBaseClass(val myArg: MyArgClass) {
#Autowired
private lateinit var myInjectedBean: MyInjectedBean
fun useBothArgAndBean()
}
class MyConcreteClass(myArg: MyArgClass) : MyBaseClass(myArg)
val myConcreteClass = MyConcreteClass(obtainMyArgClass())
myConcreteClass.useBothArgAndBean()
So I have a base class with one argument in the constructor and another argument injected from the Spring context. Currently, such a setup is not possible because Spring tries to inject also MyArgClass from context and because there's no such bean (it's constructed manually) it fails on "no matching bean".
The question is how to make this scenario work. Note that I cannot use the factory-method solution mentioned here https://stackoverflow.com/a/58790893/13015027 because I need to directly call MyBaseClass constructor from MyConcreteClass constructor. Perhaps there's a trick on how to avoid that or how to force Spring not to try to inject into the base constructor or ...?
You have a quite confusing setup there, and I am not sure that you are fully aware how injection by Spring works. You can
either create a class on your own, using its constructor, or
you can let Spring create the class and inject everything, and you don't call the constructor.
When you call the constructor, Spring will not magically inject some parts of your class, just because it has seemingly the right annotations. The variable myInjectedBean will just be null.
If you want to have the ability to create the class on your own using the constructor, you should not use field injection, because you would obviously not have any possibility to initialize the field.
Then your classes MyBaseClass and MyConcreteClass would look like this:
abstract class MyBaseClass(
val myArg: MyArgClass,
private val myInjectedBean: MyInjectedBean
) {
fun useBothArgAndBean()
}
class MyConcreteClass(myArg: MyArgClass, myInjectedBean: MyInjectedBean) : MyBaseClass(myArg, myInjectedBean)
Now, as already suggested by #Sam, you can have myInjectedBean be injected while providing myArg manually by writing another component that can completely be created by Spring, because it will only autowire myInjectedBean while myArg is provided as argument for a factory method:
#Component
class MyFactory(val myInjectedBean: MyInjectedBean) {
fun createMyConcreteClass(myArg: MyArgClass) =
MyConcreteClass(myArg, myInjectedBean)
}
Then in a class, where you have an injected myFactory: MyFactory you can just call myFactory.createMyConcreteClass(myArg) and you obtain a new MyConcreteClass that has an injected myInjectedBean.
I think you still do need some sort of factory. It would pass both the bean and the additional arguments to the MyConcreteClass constructor, and would look like this:
#Component
class MyFactory(val myInjectedBean: MyInjectedBean) {
fun getMyConcreteClass(myArg: MyArgClass) =
MyConcreteClass(myArg, myInjectedBean)
}
If using that approach, none of the other classes except MyInjectedBean would need to be registered with Spring.
In fact, it's a little surprising to me that you currently have MyBaseClass annotated with #Component. What do you expect that to do, and does it work?
Looking at the Android Architecture Blueprints "todo‑mvp‑dagger" from here: https://github.com/googlesamples/android-architecture.
I'm trying to understand the Dagger2 implementation and I can't seem to figure out how they are getting Dagger2 to provide the TasksRepository.
They have #Provides for "provideTasksLocalDataSource" and "provideTasksRemoteDataSource" but where is the one for the actual TaskRepository?
In the TodoApplication class they have this:
#Inject
TasksRepository tasksRepository;
How can it inject this without an #Provides for it anywhere?
If I try to use this same approach in my own app I get this error:
cannot be provided without an #Provides- or #Produces-annotated method
So I went looking everywhere in the Blueprints code but I can't see how they did it themselves. Is there some trick to it that allows them to not have an #Provides? It definitely builds so they've gotten around it somehow.
Someone has asked this same question on the actual github page but there are no answers at the time of writing. https://github.com/googlesamples/android-architecture/issues/561.
I'm on the latest commit which at the time of writing is "082bd72d62472f9caadd2979046067fb928bbfef".
In the repository you mentioned, Dagger 2 knows how to inject TasksRepository through the constructor being marked with #Inject. From the source:
#Inject
TasksRepository(#Remote TasksDataSource tasksRemoteDataSource,
#Local TasksDataSource tasksLocalDataSource) {
mTasksRemoteDataSource = tasksRemoteDataSource;
mTasksLocalDataSource = tasksLocalDataSource;
}
Since the constructor is annotated with #Inject, Dagger 2 will attempt to use the constructor to inject TasksRepository into consumers like TodoApplication.
Since TasksDataSource are already bound in TasksRepositoryModule there is enough information for Dagger 2 to perform injection without the burden of extra #Provides or #Binds methods.
Similarly, you can do the following:
class Foo {
private final Bar bar;
#Inject
Foo(Bar bar) {
this.bar = bar;
}
}
class Bar {
#Inject
Bar() {}
}
class Activity extends AppCompatActivity {
#Inject Foo foo;
}
And Dagger 2 will be able to inject Foo inside AppCompatActivity. Why?
Dagger 2 knows how to construct a Bar object (through calling the empty constructor)
Dagger 2 knows that when creating instances of Foo it must use the constructor annotated with #Inject with the single parameter Bar.
There are no other dependencies for Foo, in other words the complete object graph is available.
I have an import "import play.api.libs.ws.WSClient" which i want to use within my object
Object X {
...
}
But this doesn't seem to be available inside my object. I see that dependency injection is only available for classes. How do i get this to work?
Injecting a dependency into an object is impossible.
You have two options:
Ugly and deprecated: Access the injector via the global application:
val wsClient = Play.current.injector.instanceOf[WSClient]
Way to go if your code needs to live in an object: Pass the dependency in as a parameter. However this just defers the problem to the caller.
def myMethod(wsClient: WSClient) = // foo
If youre working with a legacy application where you have objects and need an injected dependency, I think one way to "improve" the situation and make a step into the right direction is to provide access to an injected class like so:
object MyObject {
private def instance = Play.current.injector.instanceOf[MyObject]
def myMethod(param: String): String =
instance.myMethod(param)
}
class MyObject #Inject() (wsClient: WSClient) {
def myMethod(param: String): String =
// foo
}
This allows legacy code to access the methods via object, while new code can inject the dependency. You may also annotate the method on the object as deprecated so that users know.
I have a simple situation:
class MyClass #Inject() (configuration: Configuration) {
val port = configuration.get[String]("port")
...
}
and now I want to use MyClass in some other object:
object Toyota extends Car {
val myClass = new MyClass(???)
...
}
but I dont know how when I use MyClass i give it the configuration instance i annotated that will be injected when MyClass is going to be instantiated..
im using play2.6/juice/scala
thanks!
First of all, you should decide if dependency injection is really what you need. Basic idea of DI: instead of factories or objects themselves creating new objects, you externally pass the dependencies, and pass the instantiation problem to someone else.
You suppose to go all in with it if you rely on framework, that is why no way of using new along with DI. You cannot pass/inject a class into the scala object, here is a draft of what you can do:
Play/guice require some preparation.
Injection Module to tell guice how to create objects (if you cannot do this if annotations, or want to do it in one place).
class InjectionModule extends AbstractModule {
override def configure() = {
// ...
bind(classOf[MyClass])
bind(classOf[GlobalContext]).asEagerSingleton()
}
}
Inject the injector to be able to access it.
class GlobalContext #Inject()(playInjector: Injector) {
GlobalContext.injectorRef = playInjector
}
object GlobalContext {
private var injectorRef: Injector = _
def injector: Injector = injectorRef
}
Specify which modules to enable, because there can be more than one.
// application.conf
play.modules.enabled += "modules.InjectionModule"
And finally the client code.
object Toyota extends Car {
import GlobalContext.injector
// at this point Guice figures out how to instantiate MyClass, create and inject all the required dependencies
val myClass = injector.instanceOf[MyClass]
...
}
A simple situation expanded with a frameworks help. So, you should really consider other possibilities. Maybe it would be better to pass the configs as an implicit parameter in your case?
For dependency injection with guice take a look at:
ScalaDependencyInjection with play and Guice wiki
Suppose I'm building a car and I have several Brake beans with different implementations
class Car {
#Inject
Car(#BrakeType(value="abs")Brake frontBrake, #BrakeType(value="nonabs")Brake rearBrake) { }
}
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
public #interface BrakeType {
String value();
}
interface Brake {}
#BrakeType(value="abs")
class AbsBrakeImpl implements Brake {
#Inject AbsBrakeImpl() {}
}
#BrakeType(value="nonabs")
class BrakeImpl implements Brake {
#Inject BrakeImpl() {}
}
why does my CarModule have to define #Provides for the specific Brake types? Shouldn't the custom annotation type #BrakeType be enough to determine which impl to inject? Or would that require using reflection, which dagger2 does not use?
#Module
public class CarModule {
#Provides #BrakeType("abs")
public Brake absBrake() {
return new AbsBrakeImpl();
}
#Provides #BrakeType("nonabs")
public Brake nonabsBrake() {
return new BrakeImpl();
}
}
Dagger doesn't look at qualifier annotations on classes, only on #Provides or #Binds methods. So the #BrakeType(value="abs") annotations on your classes don't have any effect.
A more canonical way of writing your code is:
class AbsBrakeImpl implements Brake {
#Inject AbsBrakeImpl() {}
}
class BrakeImpl implements Brake {
#Inject BrakeImpl() {}
}
#Module
abstract class CarModule {
#Binds #BrakeType("abs")
abstract Brake absBrake(AbsBrakeImpl impl);
#Binds #BrakeType("nonabs")
abstract Brake nonabsBrake(BrakeImpl impl);
}
Note that since you have #Inject on the constructors of your implementations, you can simply use Dagger's #Bind to bind the implementations directly to the appropriately qualified interface.
Reflection is probably not a big issue here because it would happen at compile time.
I did not look through the source code, but dagger is but an annotation processor—it registers to be called whenever a set of given annotations is used. While the qualifier alone would probably be enough to find out what you intended, I can think of the following reasons why this could not be the best solution.
javax.inject.Qualifier is part of a bigger API, and might also be used by other libraries in different context. So you might not want dagger to generate code for a method, just because it is annotated with a qualifier.
Another reason could be that since there is the possibility to create custom qualifiers, dagger would have to check every annotation on every method in every module and then in turn determine whether that annotation itself is annotated with #Qualifier to see if the method is of some interest to it. This is rather an unnecessary overhead.
There might be more reasons, but those 2 listed here seem enough to just make users of dagger use some sort of contract: #Provides.
Annotations don't affect the performance of the code, and having an addtional annotation won't do any harm, so there is more to gain than to lose by handling it the way they do.
For the record, you can use your own qualifier annotations (as BrakeType), or just use #Named from Dagger.
Using this last one, your code will look something like:
#Inject #Named("abs") Brake frontBrake;
#Inject #Named("nonabs") Brake rearBrake;
And on your module:
#Provides #Named("abs") static Brake provideAbsBrake() {
return new AbsBrakeImpl();
}
#Provides #Named("nonabs") static Brake provideNonAbsBrake() {
return new BrakeImpl();
}
Remember to use Dagger name conventions (like provide prefix) to get most of it. And on your modules try to use all #Provides methods static, doing so the resultant implementation does not need to instantiate it.
In short, Provides and Qualifiers work together so you need both.
Source: Dagger users guide.
#Inject constructor means provide a type that class itself, in your case, which mean you provide the AbsBrakeImpl type and BrakeImpl type, so when you try to inject with Brake, dagger can not found the provider.
Qualifier in #Inject constructor is not work, because class type is unique, we don't need to add a qualifier to.
So, in your case, or you have to use CarModule to tell Dagger explicitly, or change your constructor with
class Car {
#Inject
Car(AbsBrakeImpl frontBrake, BrakeImpl rearBrake) { }
}