Dagger: Inject field on provided pojo - java

Currently testing with dagger, what I want to do is instantiate and inject different Bar implementations. How can I inject fields in provided fields?
for example:
Module:
#Module(
injects = {
Main.class
},
complete = false,
library = true
)
public class ExampleTestModule {
#Provides
public Foo providesFoo() {
return new Foo();
}
#Provides
public Bar providesBar(BarImpl impl) {
// return new BarImpl(); // null
return impl;
}
}
Main:
public class Main {
#Inject
Foo foo;
}
Foo:
public class Foo {
#Inject
Bar bar;
}
Bar:
public interface Bar {
}
BarImpl
public class BarImpl implements Bar {
}
TestCase:
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
public void testFoo() {
Main main = new Main();
ObjectGraph.create(new ExampleTestModule()).inject(main);
assertNotNull(main.foo);
}
public void testFooBar() {
Main main = new Main();
ObjectGraph.create(new ExampleTestModule()).inject(main);
assertNotNull(main.foo.bar);
}
}
Main.Foo is not null but Main.Foo.Bar is null.

You are never injecting bar into foo.
ObjectGraph.create(new ExampleTestModule()).inject(main);
This line will only look at the fields of main which are annotated by #Inject, and inject them. There is no recursive behavior.
Fixing the problem
Let's go step-by-step:
You provided complete = false and library = true in your Module. You should only use these if really necessary. Dagger will give you warnings when something is wrong, and these properties surpress these warnings. For example, removing them raises the following warning when compiling:
Error:(11, 8) error: No injectable members on BarImpl. Do you want to add an injectable constructor? required by providesBar(BarImpl) for ExampleTestModule.
Let's add an empty injectable constructor to BarImpl, as it suggests:
public class BarImpl implements Bar {
#Inject
BarImpl(){
}
}
Compiling will give a new error:
Error:(11, 8) error: Graph validation failed: You have these unused #Provider methods:
1. ExampleTestModule.providesBar()
Set library=true in your module to disable this check.
Apparently, providesBar() is never used. That means, the bar field in Foo will never be injected. You can do two things:
Inject bar manually:
ObjectGraph graph = ObjectGraph.create(new ExampleTestModule());
graph.inject(main);
graph.inject(main.foo);
Use injectable constructors (Preferred option):
public class Foo {
Bar bar;
#Inject
Foo(Bar bar){
this.bar = bar;
}
}
Using the injectable constructor, you will now have a compile error in providesFoo(), since you don't supply a Bar instance in the Foo constructor. The nice thing about Dagger is, you can safely completely remove this method. Since Foo is annotated with #Injectable, everywhere it needs to inject a Foo instance, it uses this constructor. And when it uses this constructor, it notices it needs a Bar instance, and injects this as well.
Finally, we can remove the #Inject annotation from the Foo field in Main, and create an injectable constructor. Using ObjectGraph.get(Class<?>) we can retrieve a fully instantiated Main instance.
The result
The end result should look like this:
Module:
#Module(
injects = Main.class
)
public class ExampleTestModule {
#Provides
public Bar providesBar(BarImpl impl) {
return impl;
}
}
Main:
public class Main {
Foo foo;
#Inject
Main(Foo foo) {
this.foo = foo;
}
}
Foo:
public class Foo {
Bar bar;
#Inject
Foo(Bar bar){
this.bar = bar;
}
}
Bar:
public interface Bar {
}
BarImpl:
public class BarImpl implements Bar {
#Inject
BarImpl(){
}
}
ApplicationTest:
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
public void testFoo() {
Main main = ObjectGraph.create(new ExampleTestModule()).get(Main.class);
assertNotNull(main.foo);
}
public void testFooBar() {
Main main = ObjectGraph.create(new ExampleTestModule()).get(Main.class);
assertNotNull(main.foo.bar);
}
}
Conclusion
From the result, we can conclude some things:
Don't just add library = true and complete = false to your module. This should only be necessary when using multiple complex modules.
Try to use injectable constructors. This is what Dagger's built for, and works best. An extra perk is that you can now have your fields private, like they should be.
When using injectable constructors, you really only need to create providesXXX methods when injecting instances of interfaces, like we did with Bar and BarImpl. Because, hey, that's exactly what Dependency Injection is for, right?

Related

Android Dagger2 field not injected

So basicly am trying to use dagger to inject a Baz field into class Foo, it goes like this
Baz.java
public class Baz {
}
Foo.java
public class Foo {
#Inject Baz baz;
}
but nothing gets injected and baz still null when i run it, i even tried to write the Baz empty constructor and annotate it with #Inject but it doesn't work either
how dagger work is it able to see all my classes so when i request a class instance he can create one, or am i missing some annotation, or even worse my annotation processor not working. i added those dependencies to my app.gradle
implementation 'com.google.dagger:dagger:2.14.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.14.1'
If you want to inject something somewhere using Dagger2, it's essential to have a component, also you need a place, where you want to have Foo object (which has a Baz object itself). In addition to this, you should specify the way, how objects should be created by dagger2. In your case constructor injection is absolutely OK.
Let's assume, that our component is TestComponent and the place, where we want to have Foo object is MainActivity, then the code would be something like this:
Baz.java
public class Baz {
#Inject
public Baz() {
}
}
Foo.java
public class Foo {
#Inject
Baz baz;
#Inject
public Foo() {
}
}
TestComponent.java
#Component
public interface TestComponent {
void inject(MainActivity injector);
}
(When you create component, rebuild the project)
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Inject
Foo foo;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerTestComponent.builder().build().inject(this);
}
}

Proper way of providing dependency to spring #Bean in #Configuration

In #Configuration class it is possible to create spring bean by using method with #Bean annotation
#Component
public class Foo {
}
public class Bar {
private Foo foo;
public Bar(Foo foo) {
this.foo = foo;
}
}
#Configuration
public class BarConfig {
#Bean
public Bar bar(Foo foo) {
return new Bar(foo);
}
}
But injecting Foo to BarConfig class and using it that way can let you create Bar as well:
#Component
public class Foo {
}
public class Bar {
private Foo foo;
public Bar(Foo foo) {
this.foo = foo;
}
}
#Configuration
public class BarConfig {
#Autowired
private Foo foo;
#Bean
public Bar bar() {
return new Bar(foo); // bar() without args
}
}
Is there any difference, from spring perspective, between them? Is 2nd incorrect? What would it break or what would not work because of it?
I managed to find that with 2nd there is no visible dependency to foo from Bar but would it affect anything? Refreshing/reloading spring context would pick up the change in foo while remaking bar, wouldn't it?
The only difference between this two types of autowiring is amount of code that has to be written.
I recommend you to use the first option simply because it is shorter.
The second option is suitable in case if you have to specify #Qualifier annotation in pair with #Autowired and the been which you want to inject declared in separate configuration.

How can I make a non-assisted dependency assisted?

Suppose I have a third party class as follows:
public class MyObject {
#Inject
public MyObject(Foo foo, Bar bar) { ... }
}
Now suppose that I have a factory interface like so:
public interface MyObjectFactory {
public MyObject build(Bar bar);
}
The idea is that I wish to have a MyObjectFactory that builds a MyObject for a fixed Foo - that is, essentially adding in the #Assisted annotation on the Bar constructor parameter from the outside. Of course, manually implementing MyObjectFactory is always possible:
public class MyObjectFactoryImpl implements MyObjectFactory {
#Inject private Provider<Foo> foo;
#Override
public MyObject build(Bar bar) { return new MyObject(foo.get(), bar); }
}
But let's say that there are conditions that require me to have Guice build MyObject instances - for example, method interceptors. This seems like a job for "injecting the injector":
public class MyObjectFactoryImpl implements MyObjectFactory {
#Inject private Injector injector;
#Override
public MyObject build(Bar bar) {
Injector child = injector.createChildInjector(new AbstractModule() {
#Override
protected void configure() {
bind(Bar.class).toInstance(bar);
// Set up method interceptors for MyObject here...
}
});
return child.getInstance(MyObject.class);
}
}
This sounds evil and boiler-plate-y, so I'm wondering if there are any alternate implementations and/or a way to have Guice generate the factory impl.
First of all, it is rare that you want to be passing instances of MyObject around in your class for exactly the reasons you describe. You have no control over them, so you can't add #Assisted annotations, you can't add method interceptors, etc. etc. Plus, what happens when you want to swap out the third party library for a different implementation?
Therefore, you should be wrapping MyObject into another object.
// **Please** choose better names than this in your real code.
public class MyWrapperBackedByMyObject implements MyWrapperInterface {
private final MyObject delegate;
#Inject
MyWrapperObject(Foo foo, #Assisted Bar bar) {
delegate = new MyObject(foo, bar);
}
#NotOnWeekends // Example of how you might do method interception
public void orderPizza() {
delegate.orderPizza();
}
}
Then, remove all references to MyObject throughout your code, using the naming convention I describe above, there should only be references to MyWrapperInterface.
actually it is. Have a look Assisted Inject
Include
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-assistedinject</artifactId>
<version>${guice.version}</version>
</dependency>
Update injection with assisted
public class MyInjectedObject extends MyObject implements MyIntf {
#Inject
public MyObject(Foo foo, #Assisted Bar bar) { super(foo,bar); }
}
You have to add one more interface:
public interface MyIntf {}
In your module bind generic factory to your interface
install(new FactoryModuleBuilder()
.implement(MyIntf.class, MyInjectedObject.class)
.build(MyObjectFactory.class)
);
Now you can inject MyObjectFactory anywhere you want.
MyObject obj = myObjectFactory.build(bar);

Guice: Binding several objects with different dependencies

I have the following code using Guice bindings:
public class MyApplication {
public static void main(String[] args) {
Guice.createInjector(new AbstractModule() {
#Override
protected void configure() {
bind(Foo.class).annotatedWith(Names.named("first")).toInstance(new Foo("firstFoo"));
bind(Foo.class).annotatedWith(Names.named("second")).toInstance(new Foo("secondFoo"));
bind(Bar.class).to(BarImpl.class);
bind(MyApplication.class).asEagerSingleton();
}
});
}
private #Named("first") Bar first;
private #Named("second") Bar second;
static #Value class Foo { String name; }
static interface Bar {}
static class BarImpl implements Bar {
#Inject #Named Foo foo;
}
}
I'm trying to get a Bar object for both named Foos injected in my application. Basically, it should somehow connect the #Named on Foo with the one on Bar. I have tried several solutions, from putting #Named on everything to writing a custom Provider. The latter didn't work because I don't have access to the value of the #Named annotation inside the provider. I think the solution is somewhere in the line bind(Bar.class).to(BarImpl.class);, telling it to remember the value of the #Named annotation.
My question is, is this possible at all, and if so, how?
It is using PrivateModules. Basically:
A private module's configuration information is hidden from its environment by default. Only bindings that are explicitly exposed will be available to other modules and to the users of the injector. For more explanation see this FAQ entry.
Here is how you'd use it:
protected void configure() {
install(new PrivateModule() {
#Override
protected void configure() {
// #bind makes bindings internal to this module unlike using AbstractModule
// this binding only applies to bindings inside this module
bind(Foo.class).toInstance(new Foo("first"));
// Bar's foo dependency will use the preceding binding
bind(Bar.class).annotatedWith(Names.named("first")).to(BarImpl.class);
// if we'd stop here, this would be useless
// but the key method here is #expose
// it makes a binding visible outside as if we did AbstractModule#bind
// but the binding that is exposed can use "private" bindings
// in addition to the inherited bindings
expose(Bar.class).annotatedWith(Names.named("first"));
}
});
install(new PrivateModule() {
#Override
protected void configure() {
bind(Foo.class).toInstance(new Foo("second"));
bind(Bar.class).annotatedWith(Names.named("second")).to(BarImpl.class);
expose(Bar.class).annotatedWith(Names.named("second"));
}
});
bind(MyApplication.class).asEagerSingleton();
}
}
Now you effectively have 2 Bars each of which look like
static class BarImpl implements Bar {
#Inject Foo foo;
}
but with the power of PrivateModules have a different implementation bound for the same dependency.
Hope it makes sense.

Converting code to Spring annotations

I am in the process of converting non-Spring code into Spring (v3.2) code. I have read through the Spring documentation but I am having trouble wrapping my head around the following situation shown below.
Classes Foo and Buz are managed by Spring and are singletons. Class Bar and MyHyperlinkListener need to be managed by Spring and should be prototype (though I'm not sure how to annotate these correctly).
The instance of Buz inside of Bar is currently null because Bar is not being managed by Spring. How would these classes be correctly annotated to allow this? Does a Factory class for Bar need to be created, and what would that look like?
The constructor for Bar is being passed a 'this'(aka JFrame) from inside the Foo class. I am not sure how to modify the code to handle a 'this' in Spring. I'm guessing this is another Factory that accepts a JFrame, but I'm not sure how to code that.
#Named
public class Foo extends JFrame{
private Bar bar;
private void doSomeWork(int x){
bar = new Bar( new MyHyperlinkListener(this), x);
}
}
public class Bar extends JPanel{
#Inject
private Buz buz;
public Bar(MyHyperlinkListener mhl, int x){
}
}
public class MyHyperlinkListener implements HyperlinkListener{
private JFrame frame;
public MyHyperlinkListener(JFrame frame){
this.frame=frame;
}
//...code omitted
}
#Named
public class Buz{
}
#Configuration
public class MyConfiguration{
}
public class RunMe{
public static void main(String[] args){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("myPackage");
context.refresh();
context.registerShutdownHook();
Foo foo = context.getBean(Foo.class);
foo.setVisible(true);
}
}
You could make the Bar class #Configurable, which will allow it to be managed by Spring whenever an instance is created.
#Configurable
public class Bar extends JPanel{
#Inject
private Buz buz;
public Bar(MyHyperlinkListener mhl, int x){
}
}
Alternatively, you can use a bean factory to create your instances of Bar. This will have the same effect:
#Configuration
public class BarFactory
{
#Bean
public Bar createBar(MyHyperlinkListener mhl, int x) {
return new Bar(mhl, x);
}
}
Both methods are valid, and mainly differ in how they expose the management. They will also allow the constructor you wish to use.

Categories