Spring constructor injection and super call verbosity - java

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!

Related

Is field injection legitimate in a test class for a Spring Data repository?

I want to write an integration test for a Spring Data repository and wonder what's the preferred way to make the repository available in the test class.
When testing my own plain old services, I use something like this to avoid having to use field injection, because field injection should not be used according to Spring Data project lead Oliver Gierke:
public class myServiceTest {
private MyService myService;
#Before
public void setUp() {
myService = new MyService();
}
[...]
}
But Spring Data repositories are defined as interfaces without an implementation because the Spring magic creates the implementation dynamically.
public interface FruitRepository extends CrudRepository<Fruit, Long> {
List<Fruit> findByName(String name);
}
Therefore there is no constructor I could use like this.
Using constructor injection, as in the follwoing example, also does not work, because it yields an Exception when trying to run the test:
#Autowired
public MyServiceTest(MyService myService) {
this.myService = myService;
}
java.lang.Exception: Test class should have exactly one public zero-argument constructor
As far as I can see, the only option I'm left with is injecting the repository via field injection as in the following working example:
#RunWith(SpringRunner.class)
#SpringBootTest
public class FruitRepositoryTest {
#Autowired
private FruitRepository fruitRepository;
#Test
public void findByNameFindsAnExistingFruit() {
fruitRepository.deleteAll();
fruitRepository.save(new Fruit("Orange"));
List<Fruit> foundFruits = fruitRepository.findByName("Orange");
assertFalse(foundFruits.isEmpty());
}
#Test
public void findByNameDoesNotFindANonExistingFruit() {
fruitRepository.deleteAll();
fruitRepository.save(new Fruit("Apple"));
List<Fruit> foundFruits = fruitRepository.findByName("Orange");
assertTrue(foundFruits.isEmpty());
}
}
So, my question is: Is this a legitimate exception to the rule to avoid field injection or am I missing something? Or am I maybe overthinking this, because field injection is okay to use in test classes anyway?
For tests, this is perfectly fine.
With the current version of JUnit there is really no reasonable way to do it differently.
The point of preferring constructor injection is that other pieces of code can instantiate the class properly without using any reflection magic. But nobody but JUnit will try to instantiate your tests, so the argument is void.
The integration tests of Spring Data itself use this approach. See for example here https://github.com/spring-projects/spring-data-jpa/blob/master/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java#L97 Note that Oliver is among the authors so I guess he approves as well.

Dependency Injection - Dagger2 - Generics

I am having problems injecting a generic type interface. Not sure how to do this or google it since I don't know the exact terms to search for. Sorry if i'm completely wrong just getting started with dagger.
Basically I have a use case class
public class LoadConversations<C extends IConversation>
extends UseCase<List<C>, LoadConversations.Type> {
private final IConversationRepository<C> messageRepository;
#Inject LoadConversations(#NonNull IConversationRepository<C> messageRepository) {
this.messageRepository = messageRepository;
}
....
public enum Type {
ALL, NEWER, OLDER
}
}
With IConversationRepository being an interface.
public interface IConversationRepository<C extends IConversation> {
Observable<List<C>> conversations(LoadConversations.Type params);
}
IConversation being a blank interface and ConversationModule where i provide the IConversationRepository.
Im having problems injecting with the following code. Am i missing something or doing something completey wrong. Thanks in advance.
Trying to provide as follows:
#Provides IConversationRepository<Conversation> provideConversationRepository(
ConversationRepository conversationRepository) {
return conversationRepository;
}
And I'm trying to inject this to my presenter as
private final LoadConversations<Conversation> loadConversations;
#Inject public ConversationListPresenter(LoadConversations<Conversation> loadConversations) {
this.loadConversations = loadConversations;
}
Implementation of ConversationRepository
public class ConversationRepository implements IConversationRepository<Conversation> {
#Override public Observable<List<Conversation>> conversations(LoadConversations.Type params) {
....
}
}
Error Log:
Error:(15, 10) error: com.rbttalk.android.data.repository.ConversationRepository cannot be provided without an #Inject constructor or from an #Provides-annotated method.
com.rbttalk.android.data.repository.ConversationRepository is injected at
com.rbttalk.android.di.module.sub_modules.ConversationModule.provideConversationRepository(conversationRepository)
com.rbttalk.android.domain.repository.IConversationRepository<com.rbttalk.android.domain.models.Conversation> is injected at
com.rbttalk.android.domain.usecase.conversation.LoadConversations.<init>(arg0, …)
com.rbttalk.android.domain.usecase.conversation.LoadConversations<com.rbttalk.android.domain.models.Conversation> is injected at
com.rbttalk.android.ui.main.conversation.ConversationListPresenter.<init>(loadConversations)
com.rbttalk.android.ui.main.conversation.ConversationListPresenter is injected at
com.rbttalk.android.ui.main.conversation.ConversationListFragment.userListPresenter
com.rbttalk.android.ui.main.conversation.ConversationListFragment is injected at
com.rbttalk.android.di.component.ConversationComponent.inject(conversationListFragment)
You're very close! The error message says it all:
com.rbttalk.android.data.repository.ConversationRepository cannot be provided without an #Inject constructor or from an #Provides-annotated method.
Note that this is not IConversationRepository; you've provided a binding for that with your #Provides method (which you can eventually consider converting to a #Binds method). However, that #Provides method has a parameter, ConversationRepository, which effectively asks Dagger to create an instance of that concrete ConversationRepository type for you. You've made that binding correctly, but now Dagger needs to instantiate ConversationRepository for you, and it simply doesn't know how.
You'll need to create an #Inject-annotated constructor for ConversationRepository using the annotation type javax.inject.Inject, even if it just looks like this:
#Inject ConversationRepository() {}
This allows Dagger to know that yes, it is safe to call that constructor. (This differs from Guice, which was willing to call a public parameterless constructor including the default constructor provided by Java.) Though you are welcome to accept injector-provided parameters in that annotated constructor (which might be nice if your repository has dependencies, because then you can keep the fields final), you may also choose to simply annotate some fields with #Inject and let the injector populate those after creation.

Dagger 2 Injecting Constructors

I'm starting to use Dagger 2 in an application I'm developing but I have some questions about how Dagger 2 works.
I get the all the logic behind the #Provides methods and the #Inject annotation for initialising your dependencies, but the #Inject annotation to class constructors kind of bugs my mind.
For example:
Im my app, I have one module defined, the ContextModule, to retrieve the context of my application:
ContextModule.java
#Module
public class ContextModule {
private final Context context;
public ContextModule(Context context) {
this.context = context;
}
#Provides
public Context context() {
return this.context;
}
}
This module is used by my BaseActivityComponent:
BaseActivityComponent.java
#BaseActivityScope
#Component(modules = ContextModule.class)
public interface BaseActivityComponent {
void injectBaseActivity(BaseActivity baseActivity);
}
So far so good.. then I have an AuthController class, that depends on the context and I want to inject it in my BaseActivity. So in my AuthControllers.class I have something like:
public class AuthController {
private Context context;
#Inject
public AuthController(Context context) {
this.context = context;
}
public void auth() {
// DO STUFF WITH CONTEXT
}
}
And I inject it in my BaseActivity like:
public class BaseActivity extends AppCompatActivity {
#Inject
AuthController authController;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BaseActivityComponent component = DaggerBaseActivityComponent.builder()
.contextModule(new ContextModule(this))
.build();
component.injectBaseActivity(this);
authController.auth();
}
}
Now my question is, how does dagger knows that my AuthControllers is a dependency for BaseActivity? Just by declaring
#Inject
AuthController authController;
it's like the same thing as if I created a ControllerModule like:
#Module(includes = ContextModule.class)
public class ControllerModule {
#Provides
AuthController authController(Context context) {
return new AuthController(context);
}
}
And then in my BaseActivityComponent I would add my AuthController getter and change my dependency module to ControllersModule:
#BaseActivityScope
#Component(modules = ControllersModule.class)
public interface BaseActivityComponent {
void injectBaseActivity(BaseActivity baseActivity);
AuthController getAuthController();
}
When I call injectBaseActivity(this) it "tells" dagger that all #Inject annotations are dependencies of my class, and then it searchers my project for #Inject annotated constructors that matches that type?
I thought a good thing about Dagger 2 is that the Module files could be used as a "documentation" of my dependencies three. But if just add #Inject in all the constructors I have control of, couldn't it get a little confusing in the future, since you don't know what actually depends on what? (I mean, you know what depends on what, you just have to browse a lot of files to really find out)
Is there any best practices for when using #Inject annotations in constructors or when to add the #Provides method in Modules files?
I get that using #Inject in constructor I don't need to change the constructor definition in my Module file, but is there any downside?
Thanks.
When I call injectBaseActivity(this) it "tells" dagger that all #Inject annotations are dependencies of my class, and then it searches my project for #Inject annotated constructors that matches that type?
Exactly. But it's not done when you call injectBaseActivity, but it all happens during compile time. This is one way of annotation processing (another makes use of reflection at runtime).
When you build your project the dagger-annotation-processor you include (as a dependency)in your build.gradle file gets called with a list of all your fields, classes, etc annotated by the #Inject annotation and builds a dependency graph with it. It then resolves the graph, generating source code that provides all the dependencies for the items on the graph.
injectBaseActivity just executes the code which was generated before, and assigns all the dependencies to your object. It is proper source code, which you can read, and debug.
The reason this is a compile step—simply put—is performance and validation. (e.g. If you have some dependency cycle, you get a compile error)
how does dagger knows that my AuthControllers is a dependency for BaseActivity?
#Inject
AuthController authController;
By annotating the field #Inject dagger knows you want an AuthController. So far so good. Now dagger will look for some means to provide the controller, looking for it within the component, the components dependencies, and the components modules. It will also look whether the class can be supplied on its own, because it knows about its constructor.
How does dagger know about the objects constructor if you don't include it in any module?
#Inject
public AuthController(Context context) { /**/ }
By annotating the constructor with inject you also told dagger that there is a class called AuthController and you need a context for it to be instantiated. It is basically the same as adding it to your module.
A module #Provides method should be used if you don't have the source code to just add the #Inject annotation to the constructor, or if the object needs further initialization. Or in your case...
[...]the Module files could be used as a "documentation" of my dependencies tree [...]
Yes, of course you could do that. But as your project grows you will have to maintain a lot of unnecessary code, since the same could have been done with a simple annotation on the constructor.
Is there any best practices for when using #Inject annotations in constructors or when to add the #Provides method in Modules files?
If you want to provide different versions for a different context (e.g. implementing an interface in 2 different ways) there is also the #Binds annotation that tells dagger which class you wish to provide as implementation.
Other than that I believe you should always use constructor injection when possible. If something changes you don't have to touch any other parts of your code, and it is just less code that you write, and hence less places where you could include a bug.
Also Dagger can and does optimize a lot by knowing more, and if you implement unnecessary code it will have to work with the overhead you introduced
Of course in the end it is all up to what you think is best. After all it is you that has to work with your code ;)

How to inject implementations from another module

I'm having a project based on Dagger 2 which consists of two modules. The core module includes some interfaces and some classes that have member injections declared for these interfaces.
The actual implementations of these interfaces are included in the second module which is an Android project. So, naturally the provide methods for these are included in the Android project.
Dagger will complain during compilation about not knowing how to inject these in the core module.
Any thoughts on how to achieve this without using constructor injections?
In short, I just tried this, and it works. Be sure to check the exact error messages and make sure you are providing these interfaces and #Inject annotations are present.
There is probably just some wrong named interface or a missing annotation. Following up is a full sample using your described architecture that is compiling just fine. The issue you are currently experiencing is probably the one described in the last part of this post. If possible, you should go with the first solution though and just add those annotations.
The library
For reproducability this sample has minimalist models. First, the interface needed by my class in the library module:
public interface MyInterface {
}
Here is my class that needs that interface. Make sure to declare it in the constructor and provide the #Inject annotation!
#MyScope // be sure to add scopes in your class if you use constructor injection!
public class MyClassUsingMyInterface {
private MyInterface mMyInterface;
#Inject
public MyClassUsingMyInterface(MyInterface myInterface) {
mMyInterface = myInterface;
}
}
The idea is that the interface will be implemented by the app using MyClassUsingMyInterface and provided by dagger. The code is nicely decoupled, and my awesome library with not so many features is complete.
The application
Here need to supply the actual coupling. This means to get MyClassUsingMyInterface we have to make sure we can supply MyInterface. Let's start with the module supplying that:
#Module
public class MyModule {
#Provides
MyInterface providesMyInterface() {
return new MyInterface() {
// my super awesome implementation. MIT license applies.
};
}
}
And to actually use this, we provide a component that can inject into MyTestInjectedClass that is going to need MyClassUsingMyInterface.
#Component(modules = MyModule.class)
public interface MyComponent {
void inject(MyTestInjectedClass testClass);
}
Now we have a way to provide the requested interface. We declared that interface needed by the library class in a constructor marked with #Inject. Now I want a class that requires my awesome library class to use. And I want to inject it with dagger.
public class MyTestInjectedClass {
#Inject
MyClassUsingMyInterface mMyClassUsingMyInterface;
void onStart() {
DaggerMyComponent.create().inject(this);
}
}
Now we hit compile...and dagger will create all the factories needed.
Inject Libraries you can not modify
To just provide the full scale of dagger, this sample could also have been without actual access to the source code of the library. If there is no #Inject annotation dagger will have a hard time creating the object. Notice the missing annotation:
public class MyClassUsingMyInterface {
private MyInterface mMyInterface;
public MyClassUsingMyInterface(MyInterface myInterface) {
mMyInterface = myInterface;
}
}
In that case we have to manually provide the class. The module would be needed to be modified like the following:
#Module
public class MyModule {
#Provides
MyInterface providesMyInterface() {
return new MyInterface() {
};
}
#Provides
MyClassUsingMyInterface providesMyClass(MyInterface myInterface) {
return new MyClassUsingMyInterface(myInterface);
}
}
This introduces more code for us to write, but will make those classes available that you can not modify.

Spring autowire multiple service Implementations

I have one base interface and two implementations
public interface AnimalService
{
public void eat();
}
#Service("animalService")
#Transactional
public class AnimalServiceImpl implements AnimalService
{
#Override
public void eat()
{
System.out.println("i'm eating");
}
}
#Service("birdService")
#Transactional
public class BirdServiceImpl extends AnimalServiceImpl
{
public void fly()
{
System.out.println("i'm flying");
}
}
In my main method try to call this two service implementation in this way:
public class test
{
#Autowired
private AnimalService animalService;
#Autowired
#Qualifier("birdService")
private AnimalService birdService;
public static void main(String[] args)
{
animalService.eat();
birdService.eat();
birdService.fly();
}
}
This will give compilation error, since birdService can't find method fly(). Then I thought maybe the reason is i autowire AnimalService instead of BirdServiceImpl, So i change my autowire code from this:
#Autowired
#Qualifier("birdService")
private AnimalService birdService;
change to :
#Autowired
private BirdServiceImpl birdService;
But this will give me a runtime error, which is "can't find bean BirdServiceImpl".
I have google a lot of document, some say use #Resource. But this doesn't work for me. Some say register the bean in Spring Context, while all my bean registration is done by annotation. I don't want to touch Spring Context.
Now My solution is to add a new interface
public interface BirdService extends AnimalService
{
public void fly();
}
And let my BirdServiceImpl to implement this interface
public class BirdServiceImpl extends AnimalServiceImpl extends BirdService
{
public void fly()
{
System.out.println("i'm flying");
}
}
And my main class change to this:
public class test
{
#Autowired
private AnimalService animalService;
#Autowired
private BirdService birdService;
public static void main(String[] args)
{
animalService.eat();
birdService.eat();
birdService.fly();
}
}
Now is ok . But for me, this is not perfect. If I use plain java, i can just write single interface and multiple implementation. In the main method I can choose which implementation to use. Why in spring, i have to build a new interface for each new implementation in order to let my program run.
I want to know is there any better approach for my scenario?
In your question you are actually exposing two issues:
1. Inheritance issue
This problem doesn't depends on Spring Framework, but is due by your misconception about inheritance.
If you declare your service as AnimalService, you obviously can use it only as an AnimalService, regardless its real implementation.
If you want to use concrete implementations methods, you need to cast your object.
2. 'Autowiring a class' issue
This should normally work in Spring, so if your code doesn't work depends on your context configuration. Maybe you are also using AOP or transactions in your app,
If so, an autoproxy generator is enabled. This could cause your problem.
Take a look at this question: Spring Autowiring class vs. interface?. And note that:
When using autoproxies, you need to program to the interface, not the implementation
3. Just a note
How can you use () at the end of a Java interface/class name?
As I read in your question, you have already fixed the problem by creating an Interface for the inherited class BirdService. You only complain because you have to create a new Interface...
When I read your question, another question comes to mind: Which AOP are you using? Perhaps you have to add the CGLIB to your classpath (or Maven POM or Gradle).
Reading some of the Spring AOP documentation, I found this:
If the class of a target object that is to be proxied (hereafter
simply referred to as the target class) doesn’t implement any
interfaces, then a CGLIB-based proxy will be created. This is the
easiest scenario, because JDK proxies are interface based, and no
interfaces means JDK proxying isn’t even possible.

Categories