Tech Stack: Java-11, Spring, SpringBoot
I am trying to re-create a scenario, where Field Injection in Spring would cause Circular Dependency Error
Class A is injected to Class B
Class B is injected to Class A
At Spring Run time, I was expecting the Circular Injection Error to come but it didnt
In class CallingService, I am trying to call class A's method which refers to class B's method. But that also didnt create the Circular Dependency Error.
Class A
#Service
#Data
public class A {
#Autowired B b;
String str = "B";
public void methodA() {
System.out.println(b.str); // referring to B's instance
}
}
Class B
#Service
#Data
public class B {
#Autowired A a;
String str = "B";
public void methodB() {
System.out.println(a.str); // referring to A's instance
}
}
Class CallingService <---- (trying to create Circular Dependency Error in this class)
#Service
#Data
public class CallingService {
#Autowired A a;
public void methodCalled() {
System.out.println(a.str); // This printed successfully as "A"
}
}
My Queries:
Circular Dependency is expected at run-time or while reference-time(when code is referred while running) in Field Injection [This is clear now. It will come at SPRING's]
What changes do I do to my code to create Circular Dependency Error here using Field Injection
I am actually insisting for using Field Type Injection instead of Constructor Type Injection at my office project, as the prior is quick and eliminates verbose code.
Went through few blogs which were recommending Constructor-Dependency but found most of the points lame. Only concerning one was of Circular Dependency Error. So trying to recreate the same as a POC.
Asking your questions:
Circular dependency error is avoided with Setter or Field injection. The reason being that Spring creates the beans, but the dependencies are not injected until they are needed. In Constructor injection it will ever be a runtime error, never compile time.
Use Constructor injection. But you only get the error on runtime.
Constructor injection is way more unit test friendly than Setter or Field injection, so I also recommend you using Constructor injection.
Related
I have a Bean that is responsible for loading the project settings from a config file, and making them available to any other object that might need them:
#Component
public class ProjectSettings extends Settings{[...]}
Now, I have a bunch of component classes that over multiple steps extend an abstract class where I want to use this bean in:
#Component
public class SomeDataDbEditor extends MongoDbEditor<SomeData> {[...]}
public abstract class MongoDbEditor<T extends MongodbEntryInterface> extends MongoDbTypedAccessor<T>{[...]}
public abstract class MongoDbTypedAccessor<T extends MongodbEntryInterface> extends MongoDbAccessor {[...]}
public abstract class MongoDbAccessor {
#Autowired
protected ProjectSettings projectSettings;
public MongoDbAccessor() throws DatabaseNotConnectedException {
String databaseName = projectSettings.getMongodbDatabaseName();
[...]
}
From my understanding, this should work since the #Autowired field is protected and thus visible from the #Component class SomeDataDbEditor. However, instead I get this exception:
java.lang.IllegalStateException: Failed to load ApplicationContext
[...]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.company.project.module.some_data.database.accessor.SomeDataDbEditor]: Constructor threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:217)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:117)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:310)
... 124 more
Caused by: java.lang.NullPointerException
at io.company.project.module.database.accessor.MongoDbAccessor.<init>(MongoDbAccessor.java:26)
at io.company.project.module.database.accessor.MongoDbTypedAccessor.<init>(MongoDbTypedAccessor.java:20)
at io.company.project.module.database.accessor.MongoDbEditor.<init>(MongoDbEditor.java:19)
at io.company.project.module.some_data.database.accessor.SomeDataDbEditor.<init>(SomeDataDbEditor.java:17)
...where the referenced MongoDbAccessor.<init>(MongoDbAccessor.java:26) line is String databaseName = projectSettings.getMongodbDatabaseName();
Now, I have confirmed that the projectSettings field really is null in that case. However, I was also able to confirm that if I try to access the ProjectSettings bean in the SomeDataDbEditor, that works, and the bean gets correctly instantiated.
I know that one possible solution at this point would be to use that and just manually pass the ProjectSettings bean to the parent classes, but that would kind of defeat the point of using dependency injection in the first place. Also, I would have to adjust, like, really, really many classes for that, and I want to avoid that if at all possible.
So, does anyone have an idea why this happens here, and what I can do against it?
If you use field injection (Autowired on fields), you cannot use those fields in the constructor, since Spring can only inject the dependencies after the object was constructed, i.e. after all constructors have finished.
To circumvent that, you would either have to change to constructor injection, or alternatively do the initialization work, that you do in the constructor, in a separate method annotated with PostConstruct:
#javax.annotation.PostConstruct
public void initialize() {
}
But if you are able to change your code to constructor injection, i (as well as a lot of other people in the Spring universe) highly recommend it, since it prevents exactly such problems.
I am using dagger 2 for dependency injection and got stuck with field injection. Below is the complete scenario with code sample:
Let's say we have a class A which depends on a library B
class A {
#Inject
B b;
}
Module for B:
#Module
public class BModule {
#Provides
#Singleton
public B provideB() {
return new C.methodA();
// C - static class; C.methodA returns B
}
}
But when I try to use b in class A then I get null pointer exception but if I do the same using constructor injection then it works perfectly.
I can assure that component and other dependencies are fine as the constructor part works correctly.
A is a dependency of some other class (let's call X) and A is being initialized using constructor injection (tested). Also, X is being injected as void inject(X x);
I have 2 questions:
Is there anything I am missing out for field injection due to which it is not being injected?
I am able to successfully compile the code and get runtime exception, but dagger2 is compile-time DI then why is it unable to catch this while compiling?
P.S.: I have just shared a part of the code as there are multiple dependencies, so just trying to explain the scenario. Let me know if the question/scenario is still unclear or needs more info.
Thanks.
Field injection in dagger is a bit more complicated than constructor injection. When you use constructor injection like this
class A {
#Inject
public A(B b) {}
}
and you have provider for class B
#Module
class DaggerModule {
#Provides
B provideB() {}
}
now dagger will know how to create instance of A and pass it required constructor parameter. So everything is fine, compile successfully and works perfect.
But if we speak about field injection
class A {
#Inject
B b;
}
and have somewhere provider for B, dagger can't know how to create A instance and when inject b property (in case of manually creating instance of A by hand). To make it work you need write special method in component
#Component(DaggetModule.class)
interface DaggerComponent {
void inject(A a);
}
and somewhere in code
A a = new A();
DaggerComponent component = //TODO getDaggerComponent()
component.inject(a);
After that b property will be initialized and available for later usage. Hope, it's clear now how to make field injection work.
This is a Canonical Question because this is a common error with Dagger 2.
If your question was flagged as a duplicate please read this post carefully and make sure to understand what this error means and why it occured. If this post does not work for you make sure to include where and how you provide the mentioned classes and include the full error message in your question like the one here.
I tried to use a dependency with Dagger 2, but I receive the following error when I try to compile my project:
error: com.example.MyDependency cannot be provided without an #Inject constructor or from an #Provides-annotated method.
com.example.MyDependency is provided at
com.example.MyComponent.myDependency()
What does this mean and how can I fix it?
I have a component and tried to provide a dependency. My basic setup looks like this:
// this is the dependency I try to use
class MyDependency {}
#Component
interface MyComponent {
// I want to make it accessible to be used with my component
MyDependency myDependency();
}
tl;dr You forgot to either add an #Inject to your constructor so that Dagger can use Constructor Injection to provide the object, or you need some method in one of your Modules that creates or binds the object.
What's going on?
Have a good look at the error message: It states that you try to request a dependency but Dagger has no way to provide or create it. It simply does not know how to, because it cannot be provided without an #Inject constructor or from an #Provides-annotated method.
A close look at the error message shows the class (a) that you are trying to provide and the component (b) that needs it.
com.example.MyDependency (a) is provided at
com.example.MyComponent.myDependency() (b)
You have to make sure that (b) can create or provide (a) to fix your issue.
It looks a bit more complex if you tried to inject your dependency somewhere else, but you can still see the full stack of events—in this case a constructor injection missing a dependency. The class (a) that you are trying to provide and the location (b) where Dagger tried injecting it. It also tells you where that dependent class was created (c) and again the component (d) that failed providing (a).
com.example.MyDependency cannot be provided without an #Inject constructor or from an #Provides-annotated method.
com.example.MyDependency (a) is injected at
com.example.DependentClass.(dependency) (b)
com.example.DependentClass is provided at (c)
com.example.MyComponent.myDependency() (d)
The same applies here: Make sure that (d) knows how to provide (a) and you're good to go.
How do I fix this?
Have a look at the error as shown above. Make sure you understand where it occured and what you are trying to inject. Then tell Dagger how to provide your object.
an #Inject constructor
As the error states, you try to use MyDependency but MyComponent does not know how to do that. If we have a look at the example it becomes clear why:
class MyDependency {}
The class has no #Inject annotated constructor! And there is no other module in the component, so there is nothing Dagger could do.
If you want to use constructor injection you can just add an #Inject annotated constructor and are done. Dagger will see this constructor and know how to create your class.
class MyDependency {
#Inject
MyDependency() { /**/ }
}
That is all you have to do when you can make use of constructor injection.
from an #Provides-annotated method
The error message states a second option, which allows you to provide an object if you don't want—or can't—use constructor injection. You can also add a #Provides annotated method to a module and add this module to your component.
#Module
class MyModule {
#Provides
MyDependency provideMyDependency() {
return new MyDependency();
}
}
#Component(modules = MyModule.class)
interface MyComponent {
MyDependency myDependency();
}
This way Dagger can use your module to create and provide your dependency. It is a little bit more boilerplate than using Constructor Injection, but you will have to use Modules for everything that needs further setup or that does not have an annotated constructor, e.g. third party libraries like Retrofit, OkHttp, or Gson.
There are also other ways to provide a dependency from a component. A #SubComponent has access to its parents dependencies, and a component dependency can expose some of its dependencies to its dependent components. But at some point everything Dagger provides needs to either have an #Inject constructor or a Module providing it.
But I did add MyDependency!
Pay close attention to the details. You probably are using an interface when you are only providing the implementation, or try to use a parent class when Dagger only knows about the subclass.
Maybe you added a custom #Qualifier or used #Named("typeA") with it. To Dagger this is a completely different object! Double check that you actually provide and request the same dependency.
Read the error and make sure that you either have an #Inject annotated constructor, a module that has a #Provides method that provides that type, or a parent component that does.
What if I want to provide an implementation for my interface?
A simple example like the following shows how one class extends another:
class MyDependency extends MyBaseDependency {
#Inject MyDependency() { super(); }
}
This will inform Dagger about MyDependency, but not about MyBaseDependency.
If you have one class implementing an interface or extending a super class you have to declare that. If you provide MyDependency this does not mean that Dagger can provide MyBaseDependency. You can use #Binds to tell Dagger about your implementation and provide it when the super class is required.
#Module
interface MyModule {
#Binds
MyBaseDependency provideMyBaseDependency(MyDependency implementation);
}
I started to use constructor injection in my projects since Spring declared field injection to be deprecated. Actually, the code feels prettier and more strict, I'm ok with that.
But I encountered a pattern which seems a bit...weird and verbose to me:
I have an abstract service bean class (with #Service annotation), which has, say 2 dependencies, injected directly in the constructor:
#Autowired
public AbstractService(DependencyA depA, DependencyB depB) {
this.depA = depA;
this.depB = depB;
}
Then I have multiple services bean classes (still with #Serviceannotation) extending the abstract one.
And I don't know if there is another way but this is where I find a bit verbose and repetitive having to inject the dependencies for the parent, in each sub-class constructor:
#Service
public class ServiceA extends AbstractService {
private final DepC depC;
#Autowired
public ServiceA(DepA depA, DepB depB, DepC depC) {
super(depA, depB);
this.depC = depC;
}
}
I just wanted to know if this is the right way, and what you think about this ?
The #Autowired on AbstractService doesn't do anything. Change it to:
#Service
public class ServiceA extends AbstractService {
private final DepC depC;
#Autowired
public ServiceA(DepA depA, DepB depB, DepC depC) {
super(depA, depB);
this.depC = depC;
}
}
...
public AbstractService(DependencyA depA, DependencyB depB) {
this.depA = depA;
this.depB = depB;
}
I'm ok with this setup.
For me, the main benefits of using constructor injection is to inform the developer what are the external dependencies. I find it useful when writing unit test. When writing mocks, you just know what needs to be mocked.
An other benefit is to highlight when a Class has too many dependencies, it gives a hint that refactoring may be in order.
The alternative would be using setter injection (while keeping the informational aspect), but I've grown to enjoy constructor injection.
My answer is focusing about the "verbose and repetitive" part in your question; I let others decide how "correct" your usage of annotations is.
Even with Spring and its DI framework, in the end we are still talking about Java source code!
And in Java, if your base class only offers a constructor that takes some A and B; then of course your subclass has to make a call super(A a, B b); and of course, those values a and b have to come from somewhere!
So, what you call "verbose and repetitive" is a direct consequence of using Java.
In other words: there is no way to avoid that part!
I have an interface I with method m and two concrete implementations A and B.
public interface I{
public void m();
}
public class A implements I{
public void m(){
//
}
}
public class B implements I{
public void m(){
//
}
}
I want to know when I inject I which of the two methods will be executed
#EJB
private I service;
///
service.m();
/////
None of them, it will become into an error since the application server doesn't know which implementation to use. To avoid this, just provide the id of the class implementation, which by default is the same name of the class but starting with lower case:
//uncomment one of these
//#EJB(name="a")
//#EJB(name="b")
private I service;
None of them. The code will compile, but you won't be able to deploy it on your application server. Without specifing type of injected class, you will get an Exception similar to this:
org.jboss.weld.exceptions.DeploymentException:WELD-001409 Ambiguous dependencies
for type [...] with qualifiers [...] at injection point [...]. Possible dependencies
[...] with qualifiers [...], Managed Bean [...] with qualifiers [...]
Container (i.e. your application server) won't be able to recognize which field do you really want to inject (A or B). It cannot just guess it out of thin air. To avoid this kind of errors, provide it with annotation (called qualifier) specifying whether you want to inject class A or class B. If you want an example, you should see this article.