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.
Related
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.
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
Im interested what is the proper manner to construct object with dependencies which themselves have #Assisted params. An example will better illustrate:
Ordinarilly with dependencies that do not have #Assisted params you can simply have a complex heirarchy of objects which all have their dependencies and these get injected no problem, I can just get an instance of an object and all its dependencies will be generated and injected without me having to do anything.
But if I wanted to change it so some of the dependencies in the object hierarchy have #Assisted params then I have to create those instances myself using a factory ie:
public SomeConcreteService(#Assisted String string) {
this.string = string;
}
MyFactory myFactory = injector.getInstance(MyFactory .class);
SomeService myService = factory.getMyService("some string");
This would cause problems for the clean instantiation of the objects because I would have to manually create those instances. and pass them into the required object, which essentially renders the DI for that object redundant I think???? ie I would then need to use the new keyword and pass in all dependencies manually...
new MyComplexObject(myService, myOtherService)
How do I make it so that I don't have to manually build the object graph if one or more of the dependencies uses #Assisted parameters?
The question you need to ask yourself is, "why am I making this #Assisted, and who is going to create these objects?"
If you use the key to get an instance you need, then what you have is fine:
public class YourInjectableClass {
#Inject private MyFactory factory;
public void doStuff(String key) {
// You have a key, and your factory, so you can create the instance yourself.
SomeService service = factory.getMyService(key);
// [...]
}
}
But if you use the key to get an instance create an instance to create an instance to get what you need, then that seems problematic. That might be a better problem for child injectors:
public class YourInjectableClass {
#Inject private Injector injector;
public void doStuff(String key) {
// You need an OuterObject. So rather than calling
// new OuterObject(new InnerObject(factory.getMyService(key))), do:
OuterObject outerObject =
injector.createChildInjector(new OuterObjectModule(key))
.getInstance(OuterObject.class);
// [...]
}
}
Because your value is needed throughout the dependency tree, you can treat it as an injected dependency. This can be a little more confusing, but saves you from letting your dependencies care about instantiation details all the way down the line.
Alternatively, you can create a manual OuterObjectFactory facade which does manually call new. This may be a better solution for legacy code, but can help to follow the Single Responsibility Principle by ensuring that one class is responsible for abstracting away instantiation details.
N.B. I'm assuming that SomeConcreteService takes other dependencies that the object graph can provide. If not, then there's no reason to use injection at all: Give SomeConcreteService a public constructor and call new SomeConcreteService("your value here") where needed. Though Guice takes some pains to abstract away the use of new, it is also unnecessary to create data objects or dependency-light objects like HashMap or Date.
With Google Guice or Gin I can specify parameter with are not controlled by the dependency injection framework:
class SomeEditor {
#Inject
public SomeEditor(SomeClassA a, #Assisted("stage") SomeClassB b) {
}
}
The assisted parameter stage is specified at the time an instance of SomeEditor is created.
The instance of SomeClassA is taken from the object graph and the instance of SomeClassB is taken from the caller at runtime.
Is there a similar way of doing this in Dagger?
UPDATE: As of Dagger 2.31 from January 2021, Dagger now natively supports assisted injection, which is recommended over the Square and Auto options. (Those other options still work, but may require extra setup compared to the native option.)
class SomeEditor {
#AssistedInject public SomeEditor(
SomeClassA a, #Assisted SomeClassB b) {
// ...
}
}
#AssistedFactory interface SomeEditorFactory {
SomeEditor create(SomeClassB b);
}
(original answer)
Because factories are a separate type of boilerplate to optimize away (see mailing list discussion here), Dagger leaves it to a sister project, AutoFactory. This provides the "assisted injection" functionality Guice offers via FactoryModuleBuilder, but with some extra benefits:
You can keep using AutoFactory with Guice or Dagger or any other JSR-330 dependency injection framework, so you can keep using AutoFactory even if you switch between them.
Because AutoFactory generates code, you don't need to write an interface to represent the constructor: AutoFactory will write a brand new type for you to compile against. (You can also specify an interface to implement, if you'd prefer, or if you're migrating from Guice.)
Because all the type inspection happens at compile-time, it produces plain old Java, which doesn't have any slowness due to reflection and which works well with debuggers and optimizers. This makes the Auto library particularly useful for Android development.
Example, pulled from AutoFactory's README, which will produce a SomeClassFactory with providedDepA in an #Inject-annotated constructor and depB in a create method:
#AutoFactory
final class SomeClass {
private final String providedDepA;
private final String depB;
SomeClass(#Provided #AQualifier String providedDepA, String depB) {
this.providedDepA = providedDepA;
this.depB = depB;
}
// …
}
Just like #xsveda, I also wrote an answer about this in this other question, which I'll also reproduce here.
Today, for assisted injection with Dagger you probably want to use AssistedInject. I wrote about it in this blogpost, but I'll add a full example here to make things easier.
First thing you need are the dependencies:
compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0'
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0'
Then here's how it can look like:
class ImageDownloader #AssistedInject constructor(
private val httpClient: HttpClient,
private val executorService: ExecutorService,
#Assisted private val imageUrl: URL,
#Assisted private val callback: ImageCallback
) {
#AssistedInject.Factory
interface Factory {
fun create(imageUrl: URL, callback: ImageCallback): ImageDownloader
}
}
First thing is that instead of annotating the constructor with #Inject, we annotate it with #AssistedInject. Then we annotate the parameters that will have to go through the factory, which is the opposite of what AutoFactory expects. Finally, we need an inner factory interface annotated with #AssistedInject.Factory that has a single method that receives the assisted parameters and returns the instance we're interested in.
Unfortunately, we still have an extra step here:
#AssistedModule
#Module(includes = [AssistedInject_AssistedInjectModule::class])
interface AssistedInjectModule
We don't necessarily need a dedicated module for it, even though that's a valid option. But we can also have those annotations in another module that is already installed in the component. The nice thing here is that we only need to do it once, and after that any factory will automatically become part of the graph.
With that, you can basically inject the factory and ask for your object as you'd normally do.
Yes, please check this Square project: square/AssistedInject
Currently it is not in 1.0 yet for purpose. They wait until Dagger will introduce a public API for registering those generated Module classes automatically - see this issue. With that you won't have to reference them in your Dagger code as in this example from README:
#AssistedModule
#Module(includes = AssistedInject_PresenterModule.class)
abstract class PresenterModule {}
I have an application which should produce cars and operate with them. Car object creation is a complex process so I need a factory for each type of car. Also I want users to be able to provide their own type of cars and factories which produce them. These car types and factories should be plugged to my application as jars (probably there is a better way than jars but I don't see it).
I've come to an idea of making a common CarFactory which accepts the name of the car ("mercedes", "bmw", "nissan", etc) as an argument. CarFactory has a map where each name is mapped to its own factory class. The code looks something like this (sorry I can't provide a working copy because I'm still evaluating it and don't have a version which compiles without errors)
import scala.collection.mutable.Map
class CarFactory {
var knownCarTypes = Map[String, Class[Factory]]()
def create(carType: String) = knownCarTypes.get(carType) match {
case Some(factoryClass) => Some(factoryClass.getMethod("create").invoke(null).asInstanceOf[Car])
case None => None
}
}
}
The knownCarTypes is mutable because I want user factories to register on this map providing what type of car they are responsible for and what is the name of the factory class. So from a user class it looks like this
class Mercedes extends Car
object MercedesFactory extends Factory {
def register() {
CarFactory.knownCarTypes("mercedes") = getClass
}
def create() = new Mercedes()
}
And here is my question. I don't know how to trigger the register() method of a user factory. Is it possible? Is there a better solution than my approach?
I thought about making common trait for factories, find all loaded classes implementing the trait and trigger method via reflection. But it looks quite complex. I hope some design pattern or OOP trick can be used here. What do you think?
Thanks!
If I understand your question correctly, all you have to do is call register from the object's "body":
object MercedesFactory extends Factory {
def register() {
CarFactory.knownCarTypes("mercedes") = getClass
}
register
def create() = new Mercedes()
}
Finally I got it working via reflection. I iterated over all jars on specified path, found all classes implementing my com.example.Factory trait and triggered their register() method. For jars inspection I used Clapper ClassFinder and for invoking object method I followed Thomas Jung advice. Here is the final code
import org.apache.commons.io.FileUtils
import org.clapper.classutil.{ClassFinder, ClassInfo}
import scala.collection.JavaConverters._
def triggerFactories() {
val jars = FileUtils.iterateFiles(new File("lib"), Array[String]("jar"), true).asScala.toList
val classes = ClassFinder(jars).getClasses
val factories = ClassFinder.concreteSubclasses("com.example.Factory", classes)
factories.foreach { (factory: ClassInfo) =>
companion[Factory](factory.name).register()
}
}
def companion[T](name: String)(implicit man: Manifest[T]): T =
Class.forName(name).getField("MODULE$").get(man.erasure).asInstanceOf[T]
It's worked for me. It looks tricky but I hope it won't break anything in my application in future. Please post if there is better approach, I'll reaccept the answer.