Spring autowiring bean with mixed constructor - java

I have two beans like this:
#Component
#Scope("prototype")
class A {
A(int number, B anotherBean) {
//...
}
}
#Component
class B {
//..
}
How can I build A and have B autowired? If I use new I won't get anotherBean's value, if I use autowiring I won't get number's value.
Edit: The number is calculated at runtime, so I can't use #Value like answers suggested.

I found this question today looking for an answer myself. After some consideration, here's a solution I think I'm going with. Basically I'm baking the factory method into the bean's class. This has the advantage of keeping the 'internal' dependencies (B in this example) all inside the 'A' class, and hidden from the caller, while still allowing prototype creation using a caller's runtime value. And it doesn't require yet another class file just for a factory method.
public class A {
private A (int number, B otherBean) {
...
}
#Configuration
public static class BeanConfig {
#Autowired
private B otherBean;
#Bean
#Scope("prototype")
public A makeA(int number) {
return new A(number, otherBean);
}
}
}
Then you can request a prototype bean instance while providing only the runtime value:
applicationContext.getBean(A.class, 1);

Make use of #Valueand inject the number's value via property. Your ClassA should look something like
#Component
#Scope("prototype")
class A {
#Autowired
A(#Value("${some.property}")int number, B anotherBean) {
//...
}
}
Edit (post additional condition of runtime value for number)
You can fetch the bean from BeanFactory.getBeans method as correctly pointed out by M.Deinum in comments.

You need to annotate the desired constructor-to-be-used as #Autowired, among other ways you can find with a Google search. As for your issue with 'number', there are ways to use annotations to set parameter values as well, also not difficult to find with a Google search.

Related

How to use Spring ObjectProvider with more than one bean definition

I am using an ObjectProvider to create instances of a prototype scope bean using the getObject() method. Something like this
#Configuration
class Config {
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
SomeType typeOne() {
return new SomeType();
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
SomeType typeTwo(String param) {
return new SomeType(param);
}
}
#Service
class Service {
private ObjectProvider<SomeType> objectProvider;
public Service(
ObjectProvider<SomeType> objectProvider) {
this.objectProvider = objectProvider;
}
#Override
public String performAction() {
return getSomeType().doAction();
}
private SomeType getSomeType() {
return objectProvider.getObject();
}
}
But since there are two beans of the type that the ObjectProvider is trying to get (SomeType), I get a NoUniqueBeanDefinitionException. (And I do need the other bean of the same type, because that one I need to provide parameters using objectProvider.getObject(Object... params) )
Playing around and debugging Spring I saw that if you name your ObjectProvider exactly like your bean then it works, something like:
private ObjectProvider<SomeType> typeOne;
My question is, are there other ways to use an ObjectProvider and manage to resolve ambiguity, or is this approach the way to go?
Short answer is you just need to properly qualify the ObjectProvider you want injected, like this:
public Service(#Qualifier("typeOne") ObjectProvider<SomeType> objectProvider) {
this.objectProvider = objectProvider;
}
With Spring configuration, when you specify a bean via a method, and don't specify it's name with #Bean("NAME"), Spring uses the method name as the bean name.
Similarly, when injecting a bean that is not specified by #Qualifier("NAME"), Spring takes the injected variable as the name, if that don't exists or is not unique, you might get some exceptions informing you about this (like the NoUniqueBeanDefinitionException you facing).
So, if you match the bean name and the injected variable name you don't need to be more specific, but if you don't, #Qualifier is there to your rescue :D

Can we have a factory class as spring bean and have a factory method returning multiple spring beans based on the condition?

I want to return multiple spring beans based on the condition in the factory class.
Is this a good practice?
Any better ways to write the following piece of code?.
Any other design patterns suitable here?
Below is the code snippet:
package com.test;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
#Component
public class InstanceFactory {
#Resource(name = "instance1")
private Instance instance1;
#Resource(name = "instance2")
private Instance instance2;
public Instance getService(Condition condition) {
if (condition.one() && condition.two()) {
return instance2;
} else {
return instance1;
}
}
}
It depends on what you want to achieve. Factory Pattern is meant to create objects but what you are returning are objects already create somewhere else (Spring in this case). If you want to create beans that will be managed by Spring there are several ways:
#Conditional(YourConditionImplementation.class): This annotation added on a method of a #Configuration annotated class will allow you to create a bean when the given condition is fullfilled. Example here: https://javapapers.com/spring/spring-conditional-annotation/
You can uses as well BeanFactory to inject the definition of your bean (DefinitionBean) into the container. Example here: https://www.logicbig.com/tutorials/spring-framework/spring-core/bean-definition.html
Now, if you want an object that determine what object of type Instance fits better for some need then your approach is ok, but it is not technically a factory :)
When designing something like that I would face that solution considering two design patterns:
Strategy pattern: In order to replace repetitive if else every time you need to evaluate more instances.
Decorator pattern: Trying to make every condition as configurable as possible. They can be composed (decorated) for one or more predicates.
Considering these two pattens you might achieve something like this:
First, define which conditions will identify a given instance:
public enum InstanceType {
INSTANCE_TYPE_1(Condition::isOne, Condition::isTwo),
INSTANCE_TYPE_2(Condition::isOne, Condition::isThree),
...;
private List<Predicate<Condition>> evaluators;
#SafeVarargs
InstanceType(final Predicate<Condition>... evaluators) {
this.evaluators = Arrays.asList(evaluators);
}
public boolean evaluate(final Condition condition) {
return evaluators.stream().allMatch(it -> it.test(condition));
}
}
Then, you should link every instance implementation to an specific instance type:
#Component
public class InstanceOne implements Instance {
#Override
public InstanceType getType() {
return InstanceType.INSTANCE_TYPE_1;
}
}
Finally, a class to config where defining the relation between types and instances as EnumMap
#Configuration
public class InstanceFactoryConfig {
#Autowired
private List<Instance> instances;
#Bean
public EnumMap<InstanceType, Instance> instancesMap() {
EnumMap<InstanceType, Instance> instanceEnumMap = new EnumMap<>(InstanceType.class);
instances.forEach(i -> instanceEnumMap.put(i.getType(), i));
return instanceEnumMap;
}
}
Thus, you InstanceFactory can be replaced to something like this:
public class InstanceFactory {
#Autowire
private final EnumMap<InstanceType, Instance> instancesMap;
public void getInstance(Condition condition) {
instancesMap.get(getInstanceType(condition)).doSomething();
}
private InstanceType getInstanceType(Condition condition) {
return Arrays.stream(InstancesType.values())
.filter(evaluator -> evaluator.evaluate(condition))
.findFirst().orElseThrow(() -> new RuntimeException("Instance type not found"));
}
}
As you can see, you InstanceFactory is less prone to be modified. This means, every time you need you add a new instance implementation you only need to modify the InstanceType enum. Hope this is helps.
You can use spring existing FactoryBean interface and implement your own logic
It’s one of the best approaches to create beans in spring framework
Here is the link with example :
https://www.baeldung.com/spring-factorybean
See:
Spring Profile
The active profile is set by properties and based on the value you assign to the profile, Spring will load different beans for the same interface.
So it might be exactly what you need.

Make Bean depend on list of beans in springboot

I have following #Configuration class, in which I am declaring a #Bean that depends on an #Autowired list of beans. However, this list is not complete when I am accessing to it. All #Bean definitions been executed, except the one defined in the same class.
#Configuration
public class MyConfig {
#Autowired
List<RequiredBean> requiredBeans;
#Bean(name="ProblemHere")
public CustomObject customObject() {
log.info(requiredBeans.size()); // 1 (debugging, I can see is the one defined in "AnotherConfigClass")
}
#Bean(name="reqBeanInsideClass")
public RequiredBean reqBean() {
// this method does not get executed
return new RequiredBean();
}
}
Having other classes like;
#Configuration
public class AnotherConfigClass {
#Bean(name="ThisOneGetsExecuted")
public RequiredBean reqBean() {
// this gets executed, and therefore, added to the list
return new RequiredBean();
}
}
Probably, the easiest solution would be to add #DependsOn("reqBeanInsideClass").
However:
I wonder why it works for all #Beans defined in different classes, but not in this one.
I'm not really sure that's exactly like that, and I'm afraid later on, another #Bean does not get executed
I guess the correct approach should be something like
#DependsOn(List<RequiredBean>) // Obviously this does not work
How should I solve this?
Update
I have copied the exact same class twice, in order to see what would happen, so now I have also:
#Configuration
public class MyConfig2 {
#Autowired
List<RequiredBean> requiredBeans;
#Bean(name="ProblemHere2")
public CustomObject customObject() {
log.info(requiredBeans.size());
}
#Bean(name="reqBeanInsideClass2")
public RequiredBean reqBean() {
// this method does not get executed
return new RequiredBean();
}
}
Amazingly, by doing this, both #Beans methods (ProblemHere & ProblemHere2) are called before both reqBeanInsideClass and reqBeanInsideClass2 methods.
For some reason I guess, Springboot is able to recognize #Beans required for a class as long as they are defined in another class.
Does this sound logic to anyone?
Can you not utilize the array input for #DependsOn rather than passing singular value, since it accepts String[]? That would wait for all the beans that are explicitly declared in the array before initializing, though has to be defined manually.
#Configuration
public class MyConfig {
#Autowired
List<RequiredBean> requiredBeans;
#Bean(name="customObject")
#DependsOn({"reqBeanInsideClass", "thisOneGetsExecuted"})
public CustomObject customObject() {
log.info(requiredBeans.size());
}
#Bean(name="reqBeanInsideClass")
public RequiredBean reqBean() {
return new RequiredBean();
}
}
#Autowired list of beans will be same as a single bean of same type, it will contain all beans with that type or with that superclass via springs injection, the problem is the ordering of bean initialization is not controlled properly, #DependsOn with array bean input should resolve this!
Or
You can make CustomObject bean #Lazy, so it will be initialized only when it is used within the code after initialization is done. The bean must not be used within another non-lazy bean I think. Just call some logic where an #Autowired CustomObject is used, it should instantiate the bean at that moment, where the list will contain all possible RequiredBeans
#Lazy
#Bean(name="customObject")
public CustomObject customObject() {
log.info(requiredBeans.size());
}

What is the correct Bean's proxyMode for allowing concurrency in Spring?

I am building a library based on Spring Framework and I want to allow users invoke Library's methods in parallel.
In my main class I autowire Service class:
#Autowired
private ExportListCommand exportList;
And that's implementation for Library's method:
public ResponseContainer<ExportListResponse> exportList(ExportListOptions options) {
exportList.setoAuthClient(oAuthClient);
ResponseContainer<ExportListResponse> result = exportList.executeCommand(options);
return result;
}
ExportListCommand is defined as a Bean:
#Bean
#Scope("prototype")
public ExportListCommand exportList() {
return new ExportListCommand();
}
When I as a Library user run 2 exportList's methods in parallel Spring creates only single ExportListCommand bean since it autowired only once. But in reality I need 2 independent ExportListCommand beans. I also tried to change #Scope(value="prototype") to #Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS), but that also does not work as I need: Spring creates ExportListCommand bean for each method invocation and I lose oAuthClient value since I get new object.
I made it work only with AnnotationConfigApplicationContext.getBean() approach which I would like to avoid.
What my options are? Thanks.
I believe you are looking to work with a 'factory' object.
There are two primary ways I would consider this from a Spring standpoint.
The 'Java' way: Create a factory object that will return instances of ExportListCommand
This factory would look something like this:
class ExportListCommandFactory {
ExportListCommand newInstance() {
return new ExportListCommand();
}
}
and would be used in your method like this:
#Autowire
private ExportListCommandFactory commandFactory;
public ResponseContainer<ExportListResponse> exportList(ExportListOptions options) {
final ExportListCommand exportList = commandFactory.newInstance();
exportList.setoAuthClient(oAuthClient);
ResponseContainer<ExportListResponse> result = exportList.executeCommand(options);
return result;
}
Of course, doing this would require that you change your configuration to contain a bean that is an ExportListCommandFactory rather than an ExportListCommand.
Alternatively, you could consider...
The 'Spring' way: Use FactoryBean
The only thing you should need to do here is, in your main class, #Autowire a FactoryBean<ExportListCommand> instead of the ExportListCommand, and in your method where you need to invoke the method, consult the factory to get your instance.
#Autowire
private FactoryBean<ExportListCommand> commandFactory;
public ResponseContainer<ExportListResponse> exportList(ExportListOptions options) {
final ExportListCommand exportList = commandFactory.getObject();
exportList.setoAuthClient(oAuthClient);
ResponseContainer<ExportListResponse> result = exportList.executeCommand(options);
return result;
}
You shouldn't need to change your configuration, as FactoryBean is a special bean that will consult the ApplicationContext/BeanFactory for the instance at each invocation of getObject().

Spring: Is it possible to have a duplicate constructor with different qualifier while autowiring?

I am trying to autowire a member in a class using the constructor.
#Component
public class MyClass {
private ClassA myMember;
#Autowire
public MyClass(ClassA objectA) {
myMember = objectA;
}
}
If I have multiple sources that create beans of ClassA, is it possible to have a duplicate constructor definition that instantiates based on the bean that was autowired into this class?
I want to do something like this:
#Component
public class MyClass {
private ClassA myMember;
#Autowire
public MyClass(#Qualifier ("qualifierA") ClassA objectA) {
myMember = objectA;
}
#Autowire
public MyClass(#Qualifier ("qualifierB") ClassA objectB) {
myMember = objectB;
}
}
I tried using #Qualifier this way, but it didn't work.
Is it possible to do what I'm trying to do, with Spring? How can I disambiguate based on the name (qualifierA) or (qualifierB), if the bean definition is like:
#Bean (name = "qualifierA")
public ClassA getQualifierA() {
...
}
#Bean (name = "qualifierB")
public ClassA getQualifierB() {
...
}
You can't have two constructors with the exact same signature in a single class in Java. Nor any other programming language I've ever encountered. You might use method-injection instead, with two methods (named differently, of course), mark them as #Autowired(required = false) and use the proper #Qualifier(...) to specify the instance you want to inject. You might want to handle the case when both instances are present in the spring context, so no unexpected things happen.
The short answer is: no, that is not possible. In Java you cannot have two constructors with exactly the same signature. And also, you can assign only one value to your "myMember".
However, what are you trying to accomplish here? It seems that in some occasions MyClass needs to use "objectA" and in other occasions, you need "objectB".
For these scenarios, you should not use autowiring (you can't), but simply use explicit wiring:
#Bean
MyClass myObject() {
return new MyClass(qualifierA());
}

Categories

Resources