For context, I’m working on a Spring 4.3.30 project, not by choice, but because this is a legacy system, and I need help.
I have a configuration class in an external maven dependency (I can alter its code) that looks roughly like this:
@Configuration
@ComponentScan(basePackages = {
"com.some.stuff"
})
@Import({AnotherModuleConfig.class, })
public class ExternalDependencyConfig{
@Bean
@Named("someBean")
public Stuff abean() {
//...
return new Stuff();
}
@Bean
@Named("someOtherBean")
@DependsOn("someBean") // this should make sure "someBean" is instanciated first
public Thing bbean(@Named("someBean") Stuff theGoodStuff){
//...
return new Thing(theGoodStuff);
}
}
And I’m trying to import it into my own configuration class:
@Configuration
@ComponentScan(
basePackages = {
"com.my.stuff",
})
@Import( { MyOtherModuleConfig.class, ExternalDependencyConfig.class} )
public class MyConfiguration {
}
This configuration class lives inside a module that’s part a large spring project that inherits another template project that I have to use, and is using an xml configuration exclusively. That xml configuration lives in the module we use to handle the war. So I need to add the component-scan the package of my config classes like so in the applicationContext.xml:
<context:component-scan base-package="com.my.stuff.configuration"/>
But whenever I try to load the application context, I get this error:
springframework.beans.factory.BeanCreationException: Error creating bean with name 'bbean' defined in com.some.stuff.configuration.ExternalDependendcyConfig: 'bbean' depends on missing bean 'someBean'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'someBean'
And I have no idea why.
According to the official documentation, @Named can be used to name a bean since Spring 3.0.
According to that same documentation, @DependsOn can be used to influence the bean creation order.
(note: stackoverflow breaks my second link, just search for “If you would like to influence the startup creation order” on that page)
I have tried to replace @Named(“someBean”) with @Bean(“someBean”), but then, I’m getting this error:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.some.stuff.Stuff' available: expected single matching bean but found 2: someBean,abean
The only way to fix this for now is by doing this:
@Bean
@Named("someBean")
public Stuff abean() {
//...
return new Stuff();
}
@Bean
@Named("someOtherBean")
public Thing bbean(@Named("someBean") Stuff theGoodStuff){
//...
return new Thing(abean());
}
Problem is, I will now have two beans, one used exclusively by “someOtherBean”, and another for my applicationContext.
Another way I managed to make this work: if “MyConfiguration” is in a different package as “ExternalDependencyConfig” (they’re in the same packages, as those are sister projects). In in my main application, if I add these, it will work:
<context:component-scan base-package="com.my.stuff.configuration"/>
<context:component-scan base-package="com.some.stuff.configuration"/>
However, if I swap those two, I’m getting the “BeanCreationException” mentioned earlier, and I have idea why, as it really shouldn’t matter.
This a real headhache. I have multiple combination of the different solutions here, but nothing works like I want, which is to define a bean that relies on another bean without having errors being triggered, and use that in my own context. It feels like there is something I just don’t understand about the way spring deals with beans and initialization of the application context.