I am trying to solve a problem with the Spring DI. I have two beans (MyFirstBean & MySecondBean) that both implement a given interface (MyBean). Then I have multiple other beans (e.g. OtherBean) that I want to use with either one of the two beans. Autowiring obviously fails for OtherBean since there are multiple instances of MyBean to choose from. Is there any possibility to generically create two instances of each bean
that autowires MyBean and refer to them using qualifiers? I know this is possible by writing a configuration class but since all this is part of an API, I want to keep the overhead as low as possible.
Current Situation:
public interface MyBean {
}
#Component
public class MyFirstBean implements MyBean {
}
#Component
public class MySecondBean implements MyBean {
}
#Component
public class OtherBean {
final MyBean myBean; // error due to multiple beans
public OtherBean(MyBean myBean) {
this.myBean = myBean;
}
}
Desired Situation:
#Component
public class SomeBean {
final OtherBean myBeanUsingFirstBean; // internally autowires MyFirstBean
final OtherBean myBeanUsingSecondBean; // internally autowires MySecondBean
public SomeBean(
#FirstBeanQualifier OtherBean myBeanUsingFirstBean,
#SecondBeanQualifier OtherBean myBeanUsingSecondBean) {
this.myBeanUsingFirstBean = myBeanUsingFirstBean;
this.myBeanUsingSecondBean = myBeanUsingSecondBean;
}
}
Solution 1 :
One of the ways spring autowires beans is by name. If not specified spring will create bean using class name (with small first letter) so for MyFirstBean , bean name will be myFirstBean. Knowing that you can autowire desired bean by changing the name of the property to final MyBean myFirstBean
public interface MyBean {
}
#Component
public class MyFirstBean implements MyBean {
}
#Component
public class MySecondBean implements MyBean {
}
#Component
public class OtherBean {
// this way spring will inject instance of MyFirstBean
#Autowired
final MyBean myFirstBean ;
}
Solution 2 :
Sometimes i like to manually assign beans. So i autowire all available beans into list like so, and then later in #PostConstruct u do the logic :
#Autowired
private List<MyBean> myBeans;
Solution 3 :
Using #Qualifier annotation
public interface MyBean {
}
#Component("fooBean")
public class MyFirstBean implements MyBean {
}
#Component
public class MySecondBean implements MyBean {
}
#Component
public class OtherBean {
#Autowired
#Qualifier("fooBean")
final MyBean myFirstBean ;
}
Solution 4:
Custom annotation
#Qualifier
#Target({
ElementType.FIELD, ElementType.METHOD, ElementType.TYPE,
ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface MyBeanType {
String value();
}
public interface MyBean {
}
#MyBeanType("fooBean")
#Component()
public class MyFirstBean implements MyBean {
}
#MyBeanType("barBean")
#Component
public class MySecondBean implements MyBean {
}
#Component
public class OtherBean {
#Autowired
#MyBeanType("Foo")
final MyBean myBean ;
}
Although the asker wants to avoid writing a Configuration class, I implemented a solution using one and I want to show that it's really not so bad.
Here is my Configuration class:
#Configuration
public class ApplicationContextOtherBeanQualifier {
#Autowired
#Qualifier("myFirstBean")
private MyBean myFirstBean;
#Autowired
#Qualifier("mySecondBean")
private MyBean mySecondBean;
// Here is how you get two different instances of OtherBean
// while using the same implementation:
#Bean
public OtherBean otherBeanUsingFirstBean() {
return new OtherBean(myFirstBean);
}
#Bean
public OtherBean otherBeanUsingSecondBean() {
return new OtherBean(mySecondBean);
}
}
Now, you can fit this into your Desired Situation using the #Resource and #Qualifier annotations:
#Component
public class SomeBean {
#Resource
#Qualifier("otherBeanUsingFirstBean")
private OtherBean otherBeanUsingFirstBean; // internally autowires MyFirstBean
#Resource
#Qualifier("otherBeanUsingSecondBean")
private OtherBean otherBeanUsingSecondBean; // internally autowires MySecondBean
}
Please try this out and let me know if it works for you!
You can add #Qualifier to your beans to distinguish between the different beans. When injecting the beans you can use the specified qualifier to inject the right one.
If the code should look exactly like the OP has described, custom annotations will suffice:
#Qualifier("myFirstBean")
#Retention(RetentionPolicy.RUNTIME)
public #interface FirstBeanQualifier {}
#Qualifier("mySecondBean")
#Retention(RetentionPolicy.RUNTIME)
public #interface SecondBeanQualifier {}
#Component
public class OtherBean {
private final MyBean myBean1;
private final MyBean myBean2;
public OtherBean(#FirstBeanQualifier MyBean myBean1,
#SecondBeanQualifier MyBean myBean2) {
this.myBean1 = myBean1;
this.myBean2 = myBean2;
}
}
I think this can be the simplest thing one could do.
Create a processor requesting for an object of type MyBean
#Component
public class ProcessorFactory {
#Autowired private MyFirstBean myFirstBean;
#Autowired private MySecondBean mySecondBean;
public MyBean getProcessor(arg) {
if (arg == SomeValue1) {
return myFirstBean;
}else{
return mySecondBean;
}
}
}
The usage class would look something like this
#Service
public class SomeServiceClass{
#Autowired private ProcessorFactory processorFactory;
//Other dependencies
void doSomething(Some args){
MyBean = processorFactory.getProcessor(arg);
//Do something with the object
}
}
Related
Let's say I have a "SomeInterface" and I have two springComponentImpl that implements that "SomeInterface".
I know I can autowire both implementations, together at once, with:
#Autowire
private List<SomeInterface> springComponentsImplList;
Could I inject them in a way like this?:
#Autowire
private Map<String,SomeInterface> springComponentsImplList;
So that way I could get the implementation by a "key"? It would be perfect if that key is the class name or something and generated automatically.
The following should work out-of-the-box, where the map contains the bean names as keys and the corresponding bean instances as values:
#Autowired
private Map<String, Foo> allBeansOfType;
But you may also try to get all bean instances of a particular type along with their names using ListableBeanFactory:
private Map<String, Foo> allBeansOfType;
#Autowired
public MyClass(ListableBeanFactory beanFactory) {
this.allBeansOfType = beanFactory.getBeansOfType(Foo.class);
}
You can inject the spring beans into two separate List
First approach : If you have only each of them, then name them differently and use #Qualifier
Service Interface
public interface ServiceInterf {
}
ImplementOne
#Service("implementOne")
public class ImplementOne implements ServiceInterf {
}
ImplementTwo
#Service("implementTwo")
public class ImplementTwo implements ServiceInterf {
}
And you can use #Qualifier
#Autowired
#Qualifier("implementOne")
private List<ServiceInterf> implementOne;
#Autowired
#Qualifier("implementTwo")
private List<ServiceInterf> implementTwo;
Second approach : If you have multiple of them, then you can declare them in config class using #Qualifier 5.2. Using #Qualifier to Select Beans
#Bean
#Qualifier("implementOne")
public ServiceInterf getServiceInterf1() {
return new ImplementOne();
}
#Bean
#Qualifier("implementOne")
public ServiceInterf getServiceInterf2() {
return new ImplementOne();
}
#Bean
#Qualifier("implementTwo")
public ServiceInterf getServiceInterf3() {
return new ImplementTwo();
}
#Bean
#Qualifier("implementTwo")
public ServiceInterf getServiceInterf4() {
return new ImplementTwo();
}
And also if you want to do them into Map use constructor injection
private Map<String, List<ServiceInterf>> mapOfLists;
#Autowired
public TestMap(#Qualifier("implementOne") List<ServiceInterf> implementOne,
#Qualifier("implementTwo") List<ServiceInterf> implementTwo) {
mapOfLists = Map.of("implementOne",implementOne,"implementTwo",implementTwo);
// Map.of is from java 9
}
I'm trying to create a class that Autowire an object of type T.
#component
public class TaskScheduler<T extends TaskService>{
#Autowired
private T taskService;
}
the problem is that I have two components that extend TaskService.
#component
public class firstTaskService extends TaskService {
}
and
#component
public class secondTaskService extends TaskService {
}
so when this line is executed (ts is being created)
#Autowired
TaskScheduler<firstTaskService> ts;
I get this error :
Description:
Parameter 1 of constructor in TaskScheduler required a single bean, but 2 were found
the message I got suggested this :
Action: Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify
the bean that should be consumed.
But from what I understood, the #Primary and #Qualifier annotations make me choose 1 of the components, which not what I want because I want to use firstTaskService and secondTaskService with that same class (TaskScheduler).
How could this be done?
Edit: Clarification: My objective is to reuse the TaskScheduler class with different classes that extend the TaskService class (not to use multiple classes that extend TaskService together in TaskScheduler).
If you want to autowire all beans that extends TaskService maybe you should change the autowired field to a List:
#Component
public class TaskScheduler<T extends TaskService>{
#Autowired
private List<T> taskService;
}
In this way Spring should put in the List all autowireable beans that extends TaskService.
EDIT: since you want to dinamically select the type of TaskService the only way I've found is the following. First, redefine your TaskScheduler:
public class TaskScheduler <T extends TaskService>{
private T taskService;
public void setTaskService(T taskService) {
this.taskService = taskService;
}
}
Your TaskService and related subclasses should remain untouched. Set up a configuration class as it follows:
#Configuration
public class TaskConf {
#Autowired
private FirstTaskService firstTaskService;
#Autowired
private SecondTaskService secondTaskService;
#Bean
public TaskScheduler<FirstTaskService> firstTaskServiceTaskScheduler(){
TaskScheduler<FirstTaskService> t = new TaskScheduler<>();
t.setTaskService(firstTaskService);
return t;
}
#Bean
public TaskScheduler<SecondTaskService> secondTaskServiceTaskScheduler(){
TaskScheduler<SecondTaskService> t = new TaskScheduler<>();
t.setTaskService(secondTaskService);
return t;
}
}
And then test your TaskScheduler in this way:
#Autowired
TaskScheduler<firstTaskService> ts;
Having the following class structure:
public abstract class A {
String someProperty = "property"
public abstract void doSomething();
}
#Service
public class Aa extends A {
#Override
public abstract void doSomething() {
System.out.println("I did");
}
}
#Service
public class Ab extends A {
#Override
public abstract void doSomething() {
System.out.println("I did something else");
}
}
I need a way to tell Spring which A concrete class to Autowire in my Foo service, based on a property in a properties file.
#Service
public class Foo {
#Autowire
private A assignMeAConcreteClass;
}
And in my properties file I have this:
should-Aa-be-used: {true, false}
Remove the #Service annotation, instead write a #Bean-annotated method in a configuration class that reads the properties, and returns the appropriate A instance.
Not a new way but in your case I think that a possible suitable way would be to use
FactoryBean in the class that wants to inject the bean conditionally.
The idea is simple : you implement FactoryBean by parameterizing it with the interface of the bean that you want to inject and override getObject() to inject the wished implementation :
public class FactoryBeanA implements FactoryBean<A> {
#Autowired
private ApplicationContext applicationContext;
#Value("${should-Aa-be-used}")
private boolean shouldBeUsed;
#Override
public A getObject() {
if (shouldBeUsed) {
return applicationContext.getBean(Aa.class));
return applicationContext.getBean(Ab.class));
}
}
But FactoryBean instances are not classic beans. You have to configure it specifically.
You could configure it in a Spring Java configuration in this way :
#Configuration
public class FactoryBeanAConfiguration{
#Bean(name = "factoryBeanA")
public FactoryBeanA factoryBeanA() {
return new FactoryBeanA();
}
#Bean
public beanA() throws Exception {
return factoryBeanA().getObject();
}
}
I'm trying to unit test a camel route. The route under test extends a custom abstract RouteBuilder (I know about favouring composition over inheritance - this is maintenance code). I've set up my test as #Roman Vottner did over here. Everything works (is initialized) until I hit the first abstract class up the hierarchy. It has an #Autowired class which wasn't initialized (is null) even though it was mocked and #Autowired when the test started. Any ideas on how to solve my injection problem?
#RunWith(CamelSpringRunner.class)
#BootstrapWith(CamelTestContextBootstrapper.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = {FooRouteTest.ContextConfig.class})
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class FooRouteTest {
#Configuration
#PropertySource({"classpath:some.properties", "classpath:environment.properties"})
public static class ContextConfig extends CamelConfiguration {
#Bean
public UserServices userServices() {
return mock(UserServices.class);
} //and many more of the like
}
#Autowired
private UserServices userServices; //and all the others too
#Test
public void testAfoo() throws Exception {
//....
template.setDefaultEndpointUri("direct://getTheData");
template.sendBody(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode));
//...
}
}
in the abstract super class while debugging:
#Autowired
public ClientServices clientServices;
//...
String clientNumber=clientServices.getLoggedInNumber(); //clientServices is null and not mocked!
//...
Solved this by explicitly declaring FooRoute as a bean:
#Bean
public FooRoute fooRoute(){
return new FooRoute();
}
#Override
public List<RouteBuilder> routes() {
final List<RouteBuilder> routes = new ArrayList<>();
routes.add(fooRoute());
return routes;
}
Is possible to specify that all setter should be autowired with one annotation?
This is my class:
#Component
public class MyClass {
private static Bean1 bean1;
//...
private static BeanN beanN;
public static Bean1 getBean1() {
return bean1;
}
#Autowired
public void setBean1(Bean1 bean1) {
MyClass.bean1 = bean1;
}
//...
public static BeanN getBeanN() {
return beanN;
}
#Autowired
public void setBeanN(BeanN beanN) {
MyClass.beanN = beanN;
}
}
No. There is no such built-in annotation. Also, Spring doesn't care that your method is to be interpreted as a bean mutator (a setter). Any method can be annotated with #Autowired and Spring will try to invoke it with the appropriate arguments.
Since the whole point of Spring is dependency injection, there's no reason for you to have static fields. Just inject the bean where you need it.