Unexpected error in spring boot constructor - java

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;
}

Related

How to make a singleton by generic type with class argument in Spring?

Let's suppose I have a Wrapper with generic type:
#Component
#Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class Wrapper<T> {
private final Class<T> wrappedClass;
public Wrapper(Class<T> wrappedClass) {
this.wrappedClass = wrappedClass;
}
}
And I want to use this Wrapper with many classes (for example > 100). Is it possible to make Spring create singleton of wrapper for each generic type and pass generic class as parameter to constructor? For example, Spring must always inject the same instance of Wrapper<Foo>. If it is possible, please give example with java code configuration, but not with xml.
If I understood correctly you want to add beans of wrapper dynamically based on some criteria that some beans (like Foo / Bar) adhere to and some don't.
This is a kind of advanced stuff in spring, but in a nutshell you will have to implement a Bean Factory Post Processor that will be called automatically by spring during the startup.
This is a point where you could analyze the beans by iterating over all the "accessible" beans (like Foo / Bar and others) and for beans that should be wrapped you will create a bean definition of the wrapper, despite the fact that the wrapper itself is not a bean.
I've created a simple example to illustrate this. In my sample project I've put everything under package "wrappers":
#Wrappable
public class Foo {
}
#Wrappable
public class Bar {
}
public class ShouldNotBeWrapped {
}
Note that I've put an annotation #Wrappable - a custom annotation that will serve as a "differentiator" of what should be wrapped and what not. The processing of the annotation will be done in Bean Factory Post Processor.
The annotation is nothing special really, it should be acessible in runtime (spring is a runtime framework and be put on classes):
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface Wrappable {
}
The java config will add Foo, Bar, ShouldNotBeWrapped as beans and also Bean Factory Post Processor that I'll describe below:
#Configuration
public class WrappersJavaConfig {
#Bean
public Foo foo () {
return new Foo();
}
#Bean
public Bar bar () {
return new Bar();
}
#Bean
public ShouldNotBeWrapped shouldNotBeWrapped () {
return new ShouldNotBeWrapped();
}
#Bean
public WrappersEnrichmentBFPP wrappersEnrichmentBFPP () {
return new WrappersEnrichmentBFPP();
}
}
The Wrapper class itself for the sake of example has toString but it doesn't differ much from your wrapper presented in the question:
public class Wrapper<T> {
private T wrapped;
public Wrapper(T wrapped) {
this.wrapped = wrapped;
}
#Override
public String toString() {
return "Wrapper for" + wrapped;
}
}
And the Main class will list all the loaded beans and get their classes + call toString so that we could see that the wrappers are defined correctly:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(WrappersJavaConfig.class);
String[] names = ctx.getBeanDefinitionNames();
for(String name : names) {
Object bean = ctx.getBean(name);
if(bean.getClass().getPackage().getName().startsWith("wrappers")) {
System.out.println(ctx.getBean(name).getClass() + " ==> " + ctx.getBean(name));
}
}
}
}
Sidenote, the "if" condition in the main method is because I don't want to print the beans that spring loads by itself (infra stuff, etc) - only my beans which all reside in package "wrappers" as I've mentioned above.
Now the BeanFactoryPostProcessor - is a regular bean in a sense that it gets registered in the java config and it looks like this (your implementation might be different but the idea is the same):
public class WrappersEnrichmentBFPP implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] bddNames = beanFactory.getBeanDefinitionNames();
for(String bddName : bddNames) {
Object bean = beanFactory.getBean(bddName);
if(bean.getClass().isAnnotationPresent(Wrappable.class)) {
BeanDefinition wrappedBdd = BeanDefinitionBuilder.genericBeanDefinition(Wrapper.class)
.addConstructorArgReference(bddName)
.getBeanDefinition();
((BeanDefinitionRegistry)beanFactory).registerBeanDefinition("wrapperFor" + bddName, wrappedBdd);
}
}
}
}
So I'm getting all the beans one by one in for-each loop, then I'm asking whether the bean has an annotation "wrappable" on it in the if condition. If it has - it must be wrapped.
In this case I create an "artificial" bean definition for Wrapper and add a constuctor that will reference my bean that should be wrapped.
Then I register the bean definition by adding it to the application context.
Run the code above and you'll see the output similar to mine:
class wrappers.WrappersJavaConfig$$EnhancerBySpringCGLIB$$f88f147d ==> wrappers.WrappersJavaConfig$$EnhancerBySpringCGLIB$$f88f147d#1283bb96
class wrappers.Foo ==> wrappers.Foo#74f0ea28
class wrappers.Bar ==> wrappers.Bar#f6efaab
class wrappers.ShouldNotBeWrapped ==> wrappers.ShouldNotBeWrapped#3c19aaa5
class wrappers.WrappersEnrichmentBFPP ==> wrappers.WrappersEnrichmentBFPP#3349e9bb
class wrappers.Wrapper ==> Wrapper forwrappers.Foo#74f0ea28
class wrappers.Wrapper ==> Wrapper forwrappers.Bar#f6efaab
As you see, two last lines are lines that correspond to the wrapper beans created for the same instances of Foo and Bar but nothing was created for the ShouldNotBeWrapped bean
The APIs used are somewhat obscure and look outdated, but again its pretty advanced stuff and works at the level of spring container infra itself. Having said that, there are a lot of tutorials about BeanFactoryPostProcessor-s.
Since Using BFPPs is not a usual task, and although I've provided the solution, I don't see any real usage of it, wrappers can't be used "instead" of Foo or Bar classes, do not have their APIs, etc. Maybe you could explain why do you need wrappers over some beans. Usually people use Aspects/BeanPostProcessors (not BFPP but BPP) to wrap the class into dynamic proxy (cglib / java.lang.Proxy) and add an additional behavior, stuff like #Transactional, cache handling and so forth is implemented in spring with BeanPostProcessors, so consider checking this direction as well.
It is possible and in fact a feature in spring.
Spring can inject your dependency with the correct generic type.The following example is from spring documentation.
Suppose you have an interface
public interface Store<T>{...}
and two beans. One implements Store,one implemenets Store.
#Configuration
public class MyConfiguration {
#Bean
public StringStore stringStore() {
return new StringStore();
}
#Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}
You can declare the type with the correct type parameter and spring will inject the right bean for you.
#Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean
#Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean

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

How to #Autowired a List<Integer> in spring framework

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.

Spring Boot test case doesn't use custom conversion service

I am trying to write up an integration test case with Spring Boot Test.
I customize the ConversionService to know about the new java.time types:
#Configuration
public class ConversionServiceConfiguration {
#Bean
public static ConversionService conversionService() {
final FormattingConversionService reg = new DefaultFormattingConversionService();
new DateTimeFormatterRegistrar().registerFormatters(reg);
return reg;
}
}
and then later expect it to work:
#Component
class MyServiceConfig {
#Value("${max-watch-time:PT20s}")
private Duration maxWatchTime = Duration.ofSeconds(20);
}
When running under the normal SpringApplication.run this seems to work fine. However, in my test case:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT, classes= {
MyServiceMain.class,
AttachClientRule.class
})
public class MyTest {
#Inject
#Rule
public AttachClientRule client;
#Test(expected=IllegalArgumentException.class)
public void testBad() throws Exception {
client.doSomethingIllegal();
}
}
it blows up:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'AttachClientRule': Unsatisfied dependency expressed through constructor parameter 0:
Error creating bean with name 'MyServiceConfig': Unsatisfied dependency expressed through field 'maxWatchTime': Failed to convert value of type [java.lang.String] to required type [java.time.Duration];
nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [java.time.Duration]: no matching editors or conversion strategy found;
Peering deep into the guts of the TypeConverterDelegate that does the actual conversion, it seems to capture the ConversionService used from a field on the DefaultListableBeanFactory. Setting a watchpoint on where that field is set, I find the AbstractApplicationContext.refresh() method:
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh(); // <--- MyServiceConfig initialized here
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // <--- DefaultListableBeanFactory.conversionService set here!!!
// Last step: publish corresponding event.
finishRefresh();
So the #Value injection is happening before the ConversionService is applied to the BeanFactory. No bueno!
I've found what seems to be a workaround:
#Configuration
public class ConversionServiceConfiguration implements BeanFactoryPostProcessor {
#Bean
public static ConversionService conversionService() {
final FormattingConversionService reg = new DefaultFormattingConversionService();
new DateTimeFormatterRegistrar().registerFormatters(reg);
return reg;
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.setConversionService(conversionService());
}
}
This forces the initialization to happen earlier on, but doesn't feel like the right solution (at least it's not documented as such).
Where have I gone wrong?
Spring 4.3.0, Spring Boot 1.4.0M3
EDIT
And now I've discovered another way for it to fail! Without making the same configuration class implement EnvironmentAware:
#Override
public void setEnvironment(Environment environment) {
((AbstractEnvironment) environment).setConversionService(conversionService());
}
I find that the PropertySourcesPropertyResolver uses the wrong (default) ConversionService. This is driving me mad!
Caused by: java.lang.IllegalArgumentException: Cannot convert value [PT15s] from source type [String] to target type [Duration]
at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:94)
at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:65)
at org.springframework.core.env.AbstractPropertyResolver.getProperty(AbstractPropertyResolver.java:143)
at org.springframework.core.env.AbstractEnvironment.getProperty(AbstractEnvironment.java:546)
at com.mycorp.DoSomething.go(DoSomething.java:103)
The Spring Boot developers have confirmed that this is poorly documented and does not work as specified: https://github.com/spring-projects/spring-boot/issues/6222
Try to remove static keyword from conversionService bean definition.

Retrieve a bean in Spring using java.lang.annotation in runtime

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.

Categories

Resources