I´m currently migrating an existing project to Spring. I´ve started to use dependency injection on some newer components that are loosely coupled, the project itself is however very messy, tightly coupled and it´s impossible for me to use DI entirely and I need to change the code step by step.
The "starting point" of where dependency injection is used is a component, that itself has also some dependencies which cannot be injected and have to be passed through a constructor. See this example
#Component
public class FromHellToDependencyInjectionHeaven extends ComplicatedMessyBaseComponent{
#Autowired
private InjectedComponentFromHeavenOne componentOne;
#Autowired
private InjectedComponentFromHeavenTwo componentTwo;
private UninjectableMess uninjectableMess;
/*
* Constructor that has to stay for now because of the ComplicatedMessyBaseComponent
* clashes with #Component
*/
public FromHellToDependencyInjectionHeaven(MessyDependencyOne dependencyOne,
MessyDependencyTwo dependencyTwo, UninjectableMess uninjectableMess){
super(dependencyOne,dependencyTwo);
this.uninjectableMess = uninjectableMess;
}
}
Here comes the problem:
To use #Autowired, the component that uses the dependencies has to be either a #Service or a #Component, otherwise I get
Autowired members must be defined in valid Spring bean
(#Component|#Service|...)
But when I do that, I run into complications with the existing constructor that I cannot remove.
Class doesn't contain matching constructor for autowiring
One idea was to tell Spring to disable the constructor dependency injection for this class, but I haven´t found any possible solution for that.
How can I partially use dependency injection in my project?
Related
I have a simple Quarkus resource:
#Path("/rosters")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class RosterResource {
private final RosterService rosterService;
public RosterResource(RosterService rosterService){
this.rosterService = rosterService;
}
#GET
#Path("/{rosterId}")
public Response getRoster(#PathParam("rosterId")Long rosterId){
return Response.ok(rosterService.getRosterById(rosterId)).build();
}
}
I am trying to inject the RosterServiceinstance in my resource, but I am getting a javax.enterprise.inject.UnsatisfiedResolutionException. However, if I use the #ApplicationScoped annotation on RosterService, then everything works just fine. Is there a way of injecting the RosterService class in my resource without using annotations? In other words, is there a way of making RosterService discoverable by the Quarkus container without directly annotating the class?
Edit: looking into the CDI docs, it seems that you can manually register beans using a method with a #BuildStep annotation. However, it is not clear to me which class should contain the annotated method)
Another option would be to use a Jandex index
To the best of my knowledge, Quarkus only implements so called annotated bean discovery. That means that all CDI beans in Quarkus have to have a bean defining annotation. #ApplicationScoped is one of them.
EDIT: regarding a Jandex index, that allows you to scan for beans in additional JARs. In other words, it will only expand the set of classes that are scanned for a bean defining annotation.
When it comes to a #BuildStep method -- that is only possible in a Quarkus extension. Extensions are powerful (and indeed they can define additional beans) but also complex. You can start at https://quarkus.io/guides/building-my-first-extension, but it may feel overwhelming. It may also feel like this is not the right thing to do if you want to just make your class a bean -- and that would be true. But if your class comes from an external library that you can't change, extension makes sense.
Is there a specific reason why you don't want to annotate your service class with #ApplicationScoped (or any other of the bean discover/scope annotations)?
The only other way that I'm aware of (instead of annotations) is - as you yourself mentioned - the use of Jandex index.
Using Grails (3.x) I would like to make implementations unavailable for users of a certain plugin. Functionality provided with the plugin is made avaible with depenency injection/inversion of control.
I therefore define a public interface and a package private implementation of this interface.
Secondly I would like to register the bean through resources.groovy however this needs importing the class to use - and it's package private.
In Spring i would annotate the class itself with "#Service" making it available through dependency injection (and thus references to package private classes is avoided).
How would one overcome this in Grails (3)?
Apparently it is possible to use spring annotations: Spring - The foundation for grails
I'm experimenting with examples from this official Spring tutorials and there is a dependency on this code:
https://github.com/spring-guides/gs-async-method/tree/master/complete
If you look at the code on AppRunner.java class, I have 2 questions:
When server is starting, if I put a breakpoint in this class's constructor, seems like in the constructor, the GitHubLookupService is provided by spring, using the #Service bean that was configured. BUT, there was no #Autowired annotation on the constructor, so how in the world this constructor get called with the right dependency? It was supposed to be null.
Is it an automatic assumption of Spring Boot?
Does Spring see "private field + constructor argument, and it assumes it should look for an appropriate bean?
Is it Spring Framework or Spring boot?
Am I missing something?
As I remember, it was mendatory to provide default constructor to beans / service etc. How come this class (AppRunner) doesn't have a default constructor?
How does Spring knows that it should run the constructor with the argument?
Is it because it is the only constructor?
Starting with Spring 4.3, if a class, which is configured as a Spring bean, has only one constructor, the #Autowired annotation can be omitted and Spring will use that constructor and inject all necessary dependencies.
Regarding the default constructor: You either need the default constructor, a constructor with the #Autowired annotation when you have multiple constructors, or only one constructor in your class with or without the #Autowired annotation.
Read the #Autowired chapter from the official Spring documentation for more information.
Think of it this way... Suppose you have the following component:
#Component
public class FooService {
public FooService(Bar bar) { /*whatever*/ }
}
When Spring is scanning this class, it wants to know how it should go about constructing an instance. It's using reflection so it can get a list of all of the constructors at runtime.
In this case, it is completely unambiguous how Spring must construct this instance. There's only one constructor so there is no decision to be made, and no ambiguity at all.
If you add #Autowired here, you are not narrowing anything down, and you are not giving Spring any extra information to help make its decision - its decision is already made because there is only one candidate.
And so, as a convenience, the Spring team decided #Autowired should be optional. Since its not helping the Spring framework to make a decision, its presence is just noise.
If your component has multiple constructors then you can use #Autowired on one of them to tell Spring "use this one, not that one".
I'm currently trying to configure Spring Boot (using Java Annotations and ComponentScan) for the following scenario:
Scenario
There's an interface MyService.
I want to provide a default implementation for MyService, let's call it MyDefaultService.
If the component scan detects no other implementation for MyService, Spring should instantiate MyDefaultService as a "fallback".
If there is a different implementation of MyService present, let's say MyCustomService, then that bean should always take precedence over MyDefaultService when autowiring a dependency to MyService. In that regard, MyDefaultService should be recessive (as opposed to #Primary).
Ideally, there should not need to be an additional annotation on MyCustomService to have it "override" MyDefaultService.
Ideally, no explicitly implemented factories or factory methods should be required.
Question
The question is: how do I need to annotate the MyDefaultService class in order to achieve this?
What I tried so far to solve the problem
Annotating MyDefaultService with #ConditionalOnMissingBean(MyService.class). Didn't work because MyDefaultService is never used, even if there is no other implementation of MyService.
There is an annotation called #Primarythat solves the problem. However, it needs to reside on MyCustomService, a class that I try to keep free of additional annotations. Essentially, I need the inverse annotation of #Primary on MyDefaultService. However, I couldn't find such an annotation.
Concrete use case
I am developing a service layer in one project, and a different project will implement a web UI layer on top of it. The UI project has a dependency to the service layer project. However, for certain functionalities implemented at the service layer, I need to know which user is currently logged in at the web context. So I have to define a service interface for that in the service layer project, such that it can be implemented by the UI project. However, for testing purposes in the service-layer project, I need a default implementation of that interface. Also, in case that the UI project team forgets to implement this interface, the app should not crash, but instead instantiate the fallback bean and issue a warning.
Thanks & kind regards,
Alan
I suggest writing an implementation of FactoryBean to do this. Your FactoryBean would scan the bean factory looking for beans that implement MyService, and if it finds one it returns that bean from getObject. If it doesn't, then it can instantiate MyDefaultService directly and return that. Your factory bean then gets annotated with #Primary.
So pieces like this (pseudo-code):
public class MyServiceFactory implements FactoryBean<MyService> {
ListableBeanFactory beanFactory;
public MyService getObject() {
Map beans = beanFactory.getBeansOfType(MyService.class)
if (beans.isEmpty())
return new MyDefaultService(); // plus args, obviously
else
return get_some_bean_from_the_map
}
}
and then
#Primary
#Bean
public MyServiceFactory MyServiceFactory() {
return new MyServiceFactory();
}
Spring will automatically handle the factory bean (i.e. it will make the MyService object available as a bean for injection like normal.
This solution doesn't require any special magic, and it's fairly obvious how it works. You can also handle errant cases such as multiple MyService beans being declared.
We've been using #Autowired plus Java-based Spring configuration with some success but now, we're losing control. Everyone is starting to add autowired dependencies everywhere, creating cycles and strange bugs.
So we are considering using constructor injection and autowiring of Spring configurations.
Old:
class Bean {
#Autowired Foo foo;
}
#Configuration
#Import( FooCfg.class )
class BeanCfg {
#Bean public Bean bean() { return new Bean(); }
}
New:
class Bean {
public Bean(Foo foo) {...}
}
#Configuration
class BeanCfg {
#Autowired FooCfg fooCfg;
#Bean public Bean bean() { return new Bean(fooCfg.foo()); }
}
This works pretty well (and it drives people to split beans instead of creating monsters with 10+ constructor arguments).
But it fails when Bean has a method annotated with #Transactional since CGLIB then tries to create a proxy which fails since it can't find a no-argument constructor.
What's the solution for this?
You have a couple of possible of solutions
Introduce interfaces your classes
Upgrade the Spring Version to at least 4.0
Add protected no-arg constructor
Introduce Interfaces
When introducing interfaces for your classes you can drop the usage of CgLib. Spring will then be able to use JDK Dynamic Proxies which work around interfaces. It creates a proxy around an already existing bean instance and that proxy implements all the interfaces of the class it is wrapping. That way it doesn't matter if your class has a no-arg constructor or not.
Upgrade to Spring 4
In Spring 4.0 support was added to allow proxying of classes with a missing no-arg constructor (See SPR-10594). To enable this upgrade your Spring version and add Objenesis to your classpath, Spring 4 comes with its own repacked cglib version so that shouldn't be needed anymore.
One thing to note is that you should have a constructor with no logic if you do null checks or init logic in the constructor it might fail in the case where cglib creates an instance. I would suspect that it pass null to all the constructor arguments (or some default for primitives).
Added protected no-arg constructor
Cglib needs to be able to create an instance which is used to wrap the actual class. It should be enough to have a protected constructor in your classes so that cglib can call it.