Spring boot, error defining bean from same interface and class - java

I have a class that define a list of zipcodes. In my Application class I define two objects of the same class and interface but with different information from application.yml
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
#ConfigurationProperties(prefix = "catalog.city1")
public CatalogConfiguration city1() {
return new Catalog();
}
#Bean
#ConfigurationProperties(prefix = "catalog.city2")
public CatalogConfiguration city2() {
return new Catalog();
}
}
When I want to use one like that...
#Autowired
#Qualifier("city1")
CatalogConfiguration myCity;
...I get this error:
No qualifying bean of type [] is defined: expected single matching
bean but found 2
I checked all documentation that I could, but I didn't found where it's my mistake.
Thank you in advance

You have defined 2 beans of same type. Spring boot will simply override second bean over the first. And the way you are referencing the beans is by name. So I guess what you need to do is #Qualifier at the bean definition too with the same alias as city1 and city2. Or do it like #Bean('')

Related

How to specify sub-dependency for AutoWired Beans

I have a Spring component defined like this:
#Component
public class SearchIndexImpl implements SearchIndex {
IndexUpdater indexUpdater;
#Autowired
public SearchIndexImpl(final IndexUpdater indexUpdater) {
Preconditions.checkNotNull(indexUpdater);
this.indexUpdater = indexUpdater;
}
}
along with two implementations of the IndexUpdater interface, like:
#Component
public class IndexDirectUpdater implements IndexUpdater, DisposableBean, InitializingBean {
}
#Component
public class IndexQueueUpdater implements IndexUpdater, DisposableBean, InitializingBean {
}
If I try to auto-wire SearchIndexImpl like this:
#Autowired
private SearchIndex searchIndex;
I get the following exception:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'IndexUpdater' available: expected single matching bean but found 2: indexDirectUpdater,indexQueueUpdater
which is expected since Spring cannot tell which IndexUpdater implementation to auto-wire for the indexUpdater parameter in the constructor of SearchIndexImpl. How do I guide Spring to the bean that it should use? I understand I can use the #Qualifier annotation, but that will hard-code the index updater to one of the implementation, while I want the user to be able to specify what index updater to use. In XML, I can do something like:
<bean id="searchIndexWithDirectUpdater" class="SearchIndexImpl">
<constructor-arg index="0" ref="indexDirectUpdater"/>
</bean>
How do I do the same using Spring's Java annotations?
Use the #Qualifier annotation to specify the dependency to use :
public SearchIndexImpl(#Qualifier("indexDirectUpdater") IndexUpdater indexUpdater) {
Preconditions.checkNotNull(indexUpdater);
this.indexUpdater = indexUpdater;
}
Note that #Autowired is not needed to autowire the arg constructor of a bean since Spring 4.
To answer to your comment.
To let the class that will use the bean to define the dependency to use you could allow it to define the IndexUpdater instance to inject in the container such as :
// #Component not required any longer
public class IndexDirectUpdater implements IndexUpdater, DisposableBean, InitializingBean {
}
// #Component not required any longer
public class IndexQueueUpdater implements IndexUpdater, DisposableBean, InitializingBean {
}
Declare the bean in a #Configuration class :
#Configuration
public class MyConfiguration{
#Bean
public IndexUpdater getIndexUpdater(){
return new IndexDirectUpdater();
}
The SearchIndexImpl bean will now resolve the dependency thanks to IndexUpdater getIndexUpdater().
Here we use #Component for one bean and #Bean for its dependency.
But we could also allow a full control on the beans to instantiate by using only #Bean and by removing #Component on the 3 classes :
#Configuration
public class MyConfiguration{
#Bean
public IndexUpdater getIndexUpdater(){
return new IndexDirectUpdater();
}
#Bean
public SearchIndexImpl getSearchIndexFoo(){
return new SearchIndexImpl(getIndexUpdater());
}

Registering #Component annotated class programmatically

I'm new to spring framework, my problem is to register spring component through the spring application context I tried it with many different ways but no luck yet.
#Configuration
#ComponentScan("com.example.app")
#EnableAutoConfiguration
public class ContextDataConfiguration
{
...
}
registered it with
#PostConstruct
public void initilize()
{
AutowireCapableBeanFactory beanFactory = context.getAutowireCapableBeanFactory();
beanFactory.initializeBean( new ContextDataConfiguration(), "contextDataConfiguration" );
}
but the other beans specified in the ContextDataConfiguration class are not getting initialized with this approach.
And if I specify the ContextDataConfiguration class in the component scan it is working but it is giving me an error like
not a managed type class
Is there any alternative way to do this?
You can use the #Bean annotation in a factory method to initialize your bean. So, lets say you have a MyBean component and wants to initialize it... You can do this inside your #Configuration class:
#Bean
public MyBean myBean() {
MyBean myBean = ... // initialize your bean here
return myBean;
}
How a about
#Bean
public ContextDataConfiguration contextDataConfiguration(){
return new ContextDataConfiguration();
}
This registers an instance of ContextDataConfiguration as a bean.

Spring: Injecting an external properties into java Configurations

I've got a spring boot application (1.3.1.RELEASE) and recently experienced a strange behavior when trying to inject a #ConfigurationProperties bean into my configuration. I'm not an expert in Spring, so my question is how can this behavior be explained?
So the setup is like that:
MyApplication.java:
package me.developer;
#SpringBootApplication
public class MyApplication {
public static void main(final String... args) {
SpringApplication.run(<..>, args);
}
}
SecurityProperties.java:
package me.developer.document.security;
#Setter
#Getter
#Component
#ConfigurationProperties(prefix = "security")
public class SecurityProperties {
private List<String> apiKeys = new ArrayList<>();
}
SecurityConfiguration.java
package me.developer.document;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private SecurityProperties securityProperties;
#Override
protected void configure(final HttpSecurity http) throws Exception {
<..>
}
}
When the application is started I get an error that Spring can't inject SecurityConfiguration.securityProperties because it knows nothing about me.developer.document.security.SecurityProperties bean.
But if I rename SecurityProperties to SicurityProperties (second letter "e" --> "i") - it works! Don't ask me how did I get to that, but I'm just curious how this behavior can be explained? I.e. from my perspective it should either work or not, but not depending of the bean name, etc...
Update:
I also works if I don't touch the class name but explicitly specify the bean name like that
package me.developer.document.security;
#Setter
#Getter
#Component("securityProperties.my")
#ConfigurationProperties(prefix = "security")
public class SecurityProperties {
private List<String> apiKeys = new ArrayList<>();
}
And I don't provide any additional qualifiers during autowiring... Why is it happening?
Assuming that you have a dependency on spring-boot-starter-security, Spring Boot will be auto-configuring its own SecurityProperties bean that's also annotated with #ConfigurationProperties. Unfortunately this bean will have the same name as your bean as the class names (ignoring the package) are the same. This name clash means that Spring Boot's been will be overriding your security properties bean. You should see a warning message to that effect logged during startup.
As you have observed, changing the name of your bean so that there's no longer a name clash will solve the problem.

NoUniqueBeanDefinitionException: No qualifying bean of type : This is not returned

I have the below bean defined as part a A.jar
package abc;
#Component
public class ParentInterceptor implements ClientInterceptor {
}
I have created another bean in a different project under a different package by extending ParentInerceptor
package xyz;
#Component
public class ChildInterceptor extends ParentInterceptor {
}
In my SpringBoot app , I have a bean defined similar to below
#ComponentScan({"abc","xyz"})
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
#Bean
public Data dataRepo(ParentInterceptor p){
}
}
When I run the main method , I am expecting to see NoUniqueBeanDefinitionException as 2 beans are of same type. However I see both the beans are loaded and ParentInterceptor is being used. Is there a reason why the error is not being thrown?
EDIT
However when i did the below , I was able to see the error being thrown. However I am still unable to understand why an error wasn't thrown in the case listed above.
package xyz;
#Component
public class ChildInterceptor1 extends ChildInterceptor {
}
Application Class:
#ComponentScan({"abc","xyz"})
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
#Bean
public Data dataRepo(ChildInterceptor p){
}
}
EDIT 2
I also tried to check if the child bean indeed extending the parent using the code below -
ctx.getBeansOfType(ParentInterceptor.class)
This returns ParentInterceptor & ChildInterceptor. So I am not really sure why Spring is not returning the error!
In your first example (Parent and Child), i have the correct behaviour, which is the NoUniqueBeanDefinitionException thrown.
If you want to specify which bean to be injected, you can use the #Qualifier annotation:
#Bean
public Data dataRepo(#Qualifier("parentInterceptor") ParentInterceptor p) {
}
Optionally, you can give a name to a component:
#Component("foo")
and change the #Qualifier accordingly.

Using different java based spring context configurations with cucumber

I am trying to get Cucumber working with Spring. In our code, we are already using java based Spring configuration. I am having trouble getting it to work in the following scenario. Can someone please help?
Today , in our integration test classes we use #ContextConfiguration for each class and provide the config class that is declared with in that integration test class for loading the beans. Config class is annotated with #Configuration. Same bean could be instantiated differently in 2 different classes Config classes used in 2 different integration test classes.
So when I use Cucumber, since the Contextconfiguration differs on different classes, it looks for 'Cucumber.xml' . In the xml file, I am using component-scan to scan the cucumber step definition classes by giving the package name that these classes use (both classes have same package name) . Since all beans gets loaded in same context, Cucumber is failing to load the beans when it finds the same bean defined in these different config classes .
How do I get over this problem of creating same bean but in different ways and use them in different classes?
Please note that I am not looking for a solution that creates lot of churn from our existing coding practices, so having per-test-xml file is not an option for me.
Here is how our code looks:
Class NameAndAddressProviderIntegrationTestSteps :-
#ContextConfiguration(locations="classpath:cucumber.xml")
public class NameAndAddressProviderIntegrationTestSteps {
#Configuration
#Import({
xyz.class,
abc.class,
NameAndAddressProvider.class
})
#ImportResource({
"file:configuration/spring-configuration/abc.xml",
"file:configuration/spring-configuration/xyz.xml"
})
public static class Config {
#Bean
AccountHolderDataMap dataMap() {
AccountHolderDataMap data = new AccountHolderDataMap();
data.put(ID,
new AccountHolderData(customerID));
data.get(customerID).setCustomerplaceID(testCustomerplaceID);
return data;
}
}
#Inject
private NameAndAddressProvider provider;
#When("^I call nameandAddress provider with a 'customerId'$")
public void i_call_nameandAddress_provider_with_a_customerId() throws DependencyException {
System.out.println("Entering when method");
names = provider.getNames(customerID);
System.out.println(provider.toString());
}
......
}
Class AddressProviderIntegrationTestSteps:-
#ContextConfiguration(locations="classpath:cucumber.xml")
public class AddressProviderIntegrationTestSteps {
#Configuration
#Import({
abc.class,
xyz.class,
AddressesProvider.class
})
#ImportResource({
"file:configuration/spring-configuration/test-environment.xml",
"file:configuration/spring-configuration/test-logging-config.xml"
})
public static class Config {
#Bean
#DependsOn("Environment")
AccountHolderDataMap data() {
AccountHolderDataMap data = new AccountHolderDataMap();
data.put(testCustomerID,
new AccountHolderData(testCustomerID, testCustomerplaceID,businessType));
return data;
}
}
private static final String testCustomerID = "1234";
private static final String testMarketplaceID = "abc";
#Inject
private AddressesProvider provider;
#When("^I call AddressesProvider provider with a 'CustomerID'$")
public void i_call_AddressesProvider_provider_with_a_CustomerID() throws Throwable {
List<Address> addresses = provider.getAddresses(testCustomerID);
Log.info(addresses.get(0).toString());
assertTrue(addresses.size()==1);
}
}
And here is the nested exception I am getting:-
"nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [....AccountHolderDataMap] is defined: expected single matching bean but found 2: dataMap,data"
Appreciate your help!
I've managed multiple sources for bean-definitions. You can use this at a starting point (or others in the internet as your question is quite old)
I am using spring4, see my other cucumer post for the pom
At the stepdefs use a config.class
#ContextConfiguration(classes = { CucumberConfiguration.class })
public class StepdefsTest123 {
#Autowired bean; // from cucumberBeanContext.xml
#When("^A$")
public void a() throws Throwable {
System.out.println(bean.getFoo());
}
}
in the config-class add aditional beandefinitions
#Configuration
#ComponentScan(basePackages = "package.here.cucumber")
#ImportResource("classpath:cucumberBeanContext.xml")
public class CucumberConfiguration {
// nothing to do here
}

Categories

Resources