I have a configuration class as below:
#Configuration
public class ListConfiguration {
#Bean
public List<Integer> list() {
List<Integer> ints = new ArrayList<>();
ints.add(1);
ints.add(2);
ints.add(3);
return ints;
}
#Bean
public int number() {
return 4;
}
}
I also have a test class as below
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = ListConfiguration.class)
public class ListTest {
#Autowired
List<Integer> ints;
#Test
public void print() {
System.out.println(ints.size());
System.out.println(ints);
}
}
But the output of the print method is 1 and [4], why not 3 and [1,2,3]? Thank you very much for any help!
You've got a bean of type Integer and a bean of type List<Integer> in your application context.
Now obviously the bean you want to autowire is of type List<Integer>, which does qualify as a candidate for autowiring. To discover how Spring actually autowires fields I had to dive deep into the AutowiredAnnotationBeanPostProcessor class.
TL;DR of my investigation is that Spring will prefer to autowire objects in the following order:
Default Value using #Value
Multiple beans using a type parameter.
Individual beans that match the field type.
That means that if you're autowiring a List<Integer> Spring will attempt to autowire multiple Integer beans into the list before it will attempt to autowire a single List<Integer> bean.
You can see this behaviour in the DefaultListableBeanFactory class.
Relevant snippet below:
public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
Class<?> type = descriptor.getDependencyType();
//Searches for an #Value annotation and
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
//Handle finding, building and returning default value
}
/*
* Check for multiple beans of given type. Because a bean is returned here,
* Spring autowires the Integer bean instance.
*/
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
// Do more stuff here to try and narrow down to a single instance to autowire.
}
}
Hopefully this explains why you do need to use an #Qualifer annotation when trying to autowire a list of a type when you've got individual beans of that type in your application context.
EDIT:
It's worth noting that this is not good practice. Creating a collection of primitives or primitive wrappers and registering it as a bean is going to cause issues. The best way to do this is with #Value and define your list of primitives in a properties file, that Spring picks up.
Example:
application.properties file
list=1,2,3,4
In your config class declare the following bean:
#Bean
public ConversionService conversionService() {
return new DefaultConversionService();
}
The default conversion service is used to convert comma separated values declared in a properties file into a collection of objects with type safety.
Class to use it:
#Value("${list}")
private List<Integer> anotherList;
anotherList will contain 1,2,3 & 4 as elements in the list.
May be Spring is injecting all the Integer type beans into a List instead of Autowiring List<Integer> bean that you declared.
Probably if you add #Qualifier("list") at your injection point in your Test then it will provide the behavior you are expecting.
Related
I wrote a sample program like below.
#Configuration
public class MyclassName{
private final List<String> tableIds;
public MyclassName(
List<String> tableIds) {
this.tableIds = tableIds;
}
}
While running i am getting the below error.
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in MyclassName required a single bean, but 4 were found:
- spring.sleuth.baggage-keys: defined by method 'baggageKeys' in class path resource [org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfiguration.class]
- spring.sleuth.local-keys: defined by method 'localKeys' in class path resource [org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfiguration.class]
- spring.sleuth.propagation-keys: defined by method 'propagationKeys' in class path resource [org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfiguration.class]
- spring.sleuth.log.slf4j.whitelisted-mdc-keys: defined by method 'whiteListedMDCKeys' in class path resource [org/springframework/cloud/sleuth/autoconfig/TraceBaggageConfiguration.class]
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
Process finished with exit code 0
We are using sleuth for tracing, so pom.xml has that dependencies aswell. But this is a very basic example, why iam getting this error. Can anyone help on this?
The class MyclassName is known to the spring framework.
So spring will try to create an instance of MyclassName by using the constructor:
public MyclassName(List<String> tableIds)
The Parameter 0 of constructor in MyclassName is List<String> tableIds
Spring is trying to provide a value for this parameter but instead of only one available value, it founds 4. So Spring is not intelligent enought to choose one in your place.
The 4 available values are provided by Sleuth and are declared in the TraceBaggageConfiguration.class like follow:
#Bean(BAGGAGE_KEYS)
#ConfigurationProperties(BAGGAGE_KEYS)
List<String> baggageKeys() {...}
#Bean(LOCAL_KEYS)
#ConfigurationProperties(LOCAL_KEYS)
List<String> localKeys() {...}
#Bean(PROPAGATION_KEYS)
#ConfigurationProperties(PROPAGATION_KEYS)
List<String> propagationKeys() {...}
#Bean(WHITELISTED_MDC_KEYS)
#ConfigurationProperties(WHITELISTED_MDC_KEYS)
List<String> whiteListedMDCKeys() {...}
To solve the indecision you need to tell Spring which List<String> you realy want injected in your constructor by using an unique identifier.
First qualify (give an id) to the expected bean
#Bean("myExpectedTableIds")
List<String> myTableIdsProcucerMethod()
Then use the org.springframework.beans.factory.annotation.Qualifier annotation to tell Spring which bean you realy want:
public MyclassName(#Qualifier("myExpectedTableIds") List<String> tableIds)
public MyclassName(
List<String> tableIds) {
this.tableIds = tableIds;
}
This means that your MyclassNames, as a Configuration bean in Spring, needs to inject a bean of type List<String>, it happens that slueth's TraceBaggageConfiguration class defines some beans of type List<String>:
#org.springframework.context.annotation.Bean({"spring.sleuth.baggage-keys"})
#org.springframework.boot.context.properties.ConfigurationProperties("spring.sleuth.baggage-keys")
java.util.List<java.lang.String> baggageKeys() { /* compiled code */ }
#org.springframework.context.annotation.Bean({"spring.sleuth.local-keys"})
#org.springframework.boot.context.properties.ConfigurationProperties("spring.sleuth.local-keys")
java.util.List<java.lang.String> localKeys() { /* compiled code */ }
#org.springframework.context.annotation.Bean({"spring.sleuth.propagation-keys"})
#org.springframework.boot.context.properties.ConfigurationProperties("spring.sleuth.propagation-keys")
java.util.List<java.lang.String> propagationKeys() { /* compiled code */ }
So spring reported an error, did not know which bean to inject. You may need to use Qualifier to specify the injected bean. Or, have you defined the corresponding bean which hash a List<String> type need to be injected? What is List<String> tableIds in your MyclassName? You need to define a Bean like this:
public MyclassNamesss(
#Qualifier("test") List<String> tableIds) {
this.tableIds = tableIds;
}
#Bean("test")
public List<String> myString() {
return new ArrayList<>();
}
Or you can use, in this case, the tableId will be null:
public MyclassName(
#Autowired(required = false) List<String> tableIds) {
this.tableIds = tableIds;
}
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());
}
So, I have some generic components that use reflection to initialize themselves and by doing so, they require Class<T> objects at instanciation time. Those components use annotations in order to generate useful metadata and/or convert the object to another representation more appropriate for the task at hand.
I reduced my issue down to this sample component :
#Component
public class Instantiator<T> {
final Class<T> klass;
#Autowired
public Instantiator(Class<T> klass) {
this.klass = klass;
}
public T instantiate() {
try {
return klass.newInstance();
} catch (InstantiationException|IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
Spring does not know how to automatically inject Class<T> instances, so I tried writing the following boilerplate code for each T for which I want Class<T> to be available.
#Bean
Class<Instantiatee> instantiateeClass() {
return Instanciatee.class;
}
It does not work.
Spring since version 4 has support for Autowiring generic types, but in my case, it has to infer what T is assigned to in Class<T>. Since by default Spring creates singleton beans and therefore could not possibly infer an appropriate T, I tried adding #Scope("prototype") but I ended up with a ClassCastException since the container does not know how to infer T anyway.
So, I removed the #Component annotation from Instantiator and settled on this workaround for each T I have :
#Bean
Instantiator<Instantiatee> instantiator() {
return new Instantiator<>(Instantiatee.class);
}
Do you know a workaround to make this work so that T will be inferred each time I want an Instantiator or another generic component depending on it ?
FYI, we are using spring 4.1.4 with boot.
I posted a more complete sample there : https://gist.github.com/anonymous/79e1a7ebe7c25c00a6c2.
Defining beans with #Bean, you give the bean name in the method name by default - in your case getInstanciateeClass. Also, when autowiring the default bean name is considered the parameter name, in your case klass. Because of this, Spring cannot match the beans, since they have different names and most probably there are more than one Class instances in the ApplicationContext. It does not matter if one is Class<Foo> and another one is Class<Bar>, Spring sees them as Class so it cannot do autowiring by type.
You can fix this by using the same default name both when defining the bean and when autowiring it.
#Autowired
public Instanciator(Class<T> klass) {
this.klass = klass;
}
#Bean
Class<Instanciatee> klass() {
return Instanciatee.class;
}
You can also specify the name of the bean in the #Bean annotation:
#Autowired
public Instanciator(Class<T> klass) {
this.klass = klass;
}
#Bean(name = "klass")
Class<Instanciatee> getInstanciateeClass() {
return Instanciatee.class;
}
Or you can also give the bean name when autowiring:
#Autowired
#Qualifier("getInstanciateeClass")
public Instanciator(Class<T> klass) {
this.klass = klass;
}
#Bean
Class<Instanciatee> getInstanciateeClass() {
return Instanciatee.class;
}
I have a list of annotations which I have retrieved using
java.lang.reflect.Method.getDeclaredAnnotations()
Using these annotations, how can I retrieve spring beans which are tagged
with these annotations?
Pseudo code:
public interface Work{...}
#Component
#A1
public class SpringBean1 implements Work{}
#Component
#A2
public class SpringBean2 implements Work{}
public class Test
{
public void doSomething()
{
Annotation[] annotations = getAnnotationsListUsingReflection();
for(Annotation annotation: annotations)
{
Work work = /* Retrieve bean using annotation, how to do this? */
work.xyz();
......
}
}
}
Assuming your annotations are annotating the bean types and not their methods, you can use ListableBeanFactory#getBeanNamesForAnnotation(Class).
Find all names of beans whose Class has the supplied Annotation type,
without creating any bean instances yet.
AnnotationConfigApplicationContext is one implementation of that interface.
Given such an instance
AnnotationConfigApplicationContext ctx = ...;
String[] beanNames = ctx.getBeanNamesForAnnotation(Annotation.class);
Then iterate through the bean names and get each bean
for (String beanName : beanNames) {
Object bean = ctx.getBean(beanName);
// use it
}
Alternatively, ListableBeanFactory#getBeansWithAnnotation(Class) can do the work for you:
Find all beans whose Class has the supplied Annotation type, returning
a Map of bean names with corresponding bean instances.
I have a bean Item<T> which is required to be autowired in a #Configuration class.
#Configuration
public class AppConfig {
#Bean
public Item<String> stringItem() {
return new StringItem();
}
#Bean
public Item<Integer> integerItem() {
return new IntegerItem();
}
}
But when I try to #Autowire Item<String>, I get following exception.
"No qualifying bean of type [Item] is defined: expected single matching bean but found 2: stringItem, integerItem"
How should I Autowire generic type Item<T> in Spring?
Simple solution is to upgrade to Spring 4.0 as it will automatically consider generics as a form of #Qualifier, as below:
#Autowired
private Item<String> strItem; // Injects the stringItem bean
#Autowired
private Item<Integer> intItem; // Injects the integerItem bean
Infact, you can even autowire nested generics when injecting into a list, as below:
// Inject all Item beans as long as they have an <Integer> generic
// Item<String> beans will not appear in this list
#Autowired
private List<Item<Integer>> intItems;
How this Works?
The new ResolvableType class provides the logic of actually working with generic types. You can use it yourself to easily navigate and resolve type information. Most methods on ResolvableType will themselves return a ResolvableType, for example:
// Assuming 'field' refers to 'intItems' above
ResolvableType t1 = ResolvableType.forField(field); // List<Item<Integer>>
ResolvableType t2 = t1.getGeneric(); // Item<Integer>
ResolvableType t3 = t2.getGeneric(); // Integer
Class<?> c = t3.resolve(); // Integer.class
// or more succinctly
Class<?> c = ResolvableType.forField(field).resolveGeneric(0, 0);
Check out the Examples & Tutorials at below links.
Spring Framework 4.0 and Java Generics
Spring and Autowiring of Generic Types
If you dont want to upgrade to Spring 4 you have to autowire by name as below :
#Autowired
#Qualifier("stringItem")
private Item<String> strItem; // Injects the stringItem bean
#Autowired
#Qualifier("integerItem")
private Item<Integer> intItem; // Injects the integerItem bean
Below is a solution I made to answer this question:
List<String> listItem= new ArrayList<>();
ResolvableType resolvableType = ResolvableType.forClassWithGenerics(List.class, String.class);
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setTargetType(resolvableType);
beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
beanDefinition.setAutowireCandidate(true);
DefaultListableBeanFactory bf = (DefaultListableBeanFactory) configurableWebApplicationContext.getBeanFactory();
bf.registerBeanDefinition("your bean name", beanDefinition);
bf.registerSingleton("your bean name", listItem);
Spring autowired strategy is defined in your configration file(application.xml).
if you don't defined, default is by Type, spring inject use JDK reflect mechanism.
so List?String? and List?Item?, the type is same List.class, so spring confused how to inject.
and as above persons response, you should be point #Qualifier to tell spring which bean should be inject.
i like spring configration file to define bean rather then Annotation.
<bean>
<property name="stringItem">
<list>
<....>
</list>
</property>
Spring 4.0 is the answer with the #Qualifier annotation usage. Hope this helps
I believe it has nothing to with generics... If you are injecting two different beans of same type then you need to provide a qualifier to help Spring identify them;
... Elsewhere
#Configuration
#Bean
public Item stringItem() {
return new StringItem();
}
#Bean
public Item integerItem() {
return new IntegerItem();
}
If you have a non-generic declarations like these then you need to add qualifier to help Spring identify them...
#Autowired
**#Qualifier("stringItem")**
private Item item1;
#Autowired
**#Qualifier("integerItem")**
private Item item2;
Of course, in versions 4 and above spring considers the Generic Types through the resolvers which is very cool...