Bean overriding in Spring context that uses both annotation and xml config - spring

There is a spring project A which is completely annotation based.
I need to override some beans conditionally in project B which is a legacy application using Spring 4.1.3 and uses xml based config.
There is FooConfig which is configuring beans using #ComponentScan. This config is a third party code for me. i.e I do not have access for this
#ComponentScan(basePackages = {"com.foo.bean"})
#Configuration
public class FooConfig {
}
I have created a BarConfig at my end, which imports this FooConfig and overrides some beans based on a condition. This is achieved using #Conditional
#Configuration
#Import(FooConfig.class)
public class BarConfig {
#Bean(name="helloService")
#Conditional(IsSpanishCondition.class)
public HelloService getHelloService() {
return new HelloService() {
#Override
public String getGreeting(String name) {
return "Hola "+name;
}
};
}
}
And I have included BarConfig in my application-context.xml
<context:annotation-config/>
<bean class="com.foo.config.BarConfig"/>
While this approach works flawlessly in Spring 5.1.2.RELEASE, it does not work in Spring 4.1.3.RELEASE
00:14:20.617 [main] INFO org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader - Skipping bean definition for [BeanMethod:name=getHelloService,declaringClass=com.foo.config.BarConfig]: a definition for bean 'helloService' already exists. This top-level bean definition is considered as an override.
Also, I have observed the same issue in Spring 4 in a completely annotation based context as well. i.e. it is not because of xml and annotation config mix but due to the Spring versions used here
Questions
What changed in Spring 5?
Is there any rule of thumb while working with a Spring application that uses both xml and annotation config especially when it comes to overriding the beans?
Also FTR, these are the solutions that worked
1.Overriding the beans using BeanPostProcessor
2.Using profiles. But this wouldn't work for complicated conditions.
#Profile("ENGLISH")
#Configuration
#Import(FooConfig.class)
public class EnglishConfig {
}
#Profile("SPANISH")
#Configuration
public class SpanishConfig {
#Bean(name="helloService")
public HelloService getHelloService() {
return new HelloService() {
#Override
public String getGreeting(String name) {
return "Hola "+name;
}
};
}
}

The issue here is that you are trying to override a xml bean from a #Configuration class, now I'm not 100% sure, but in spring 4 a xml bean still had precedence in choosing a bean, so the #Configuration beans would not get permission to overwrite the xml bean. Which was resolved in spring 5.
Your approach to use BeanPostProcessor is i guess the only viable solution for this.
I'm thinking maybe you could use a different bean name, implement your own behaviour and use #Qualifier annotation to choose which bean will get selected?

Related

when using functional bean registration, is there a way to inform Spring that ClassA is responsible for creating an instance of BeanA?

I've switched a portion of a Spring app to use functional bean registrations. The motivation for the switch is due to requiring multiple instances of some beans under certain conditions. It also turns out to be much more concise (which won't be at all apparent with the simplistic examples below).
The code used to look like this (simple example):
#Configuration
public class ConfigA {
#Bean
public BeanA beanA() {
return new BeanA();
}
}
#Service
public class Service1 {
#Autowired BeanA beanA;
...
}
#Service
public class Service2 {
#Autowired BeanA beanA;
...
}
I've switched the configuration class to look like this:
#Configuration
public class ConfigA implements ApplicationContextInitializer<GenericApplicationContext> {
#Override
public void initialize(GenericApplicationContext context) {
context.registerBean("beanA", BeanA.class, () -> new BeanA());
}
}
The issue I'm now encountering is that Spring is complaining about autowired beans not being found. With the original code, Spring could determine that a BeanA bean was declared via ConfigA and would create that bean before initializing the services.
With the new code, I guess there is no way for Spring to determine where the BeanA bean(s) are being declared, and so it tries to init the services before the BeanA is initialized (which causes the app to not start).
I was hoping that Spring would prioritize #Configuration classes over #Service or #Controller classes, but that doesn't seem to be the case.
I could annotate all the services with #DependsOn("configA"), but there are many services that autowire BeanA (some in other code bases), so the #DependsOn option isn't really realistic.
Question: When using functional bean registration, is there a way to inform Spring that ConfigA is responsible for creating an instance of BeanA?
In order to use the functional style of bean registration and enable autowiring mechanism in other beans you can do the following:
Remove #Configuration annotation from your ConfigA class;
Create directory named META-INF under main/java/resources and create a file named spring.factories under the newly created directory;
Fill the newly created file with the line org.springframework.context.ApplicationContextInitializer=(package-name-to-configA-class).ConfigA
Now Spring should be able to successfully autowire bean named BeanA where requested.

How can I set the default behavior of lazy init in Spring Boot?

I am working on my first Spring Boot application and I have the following problem.
I want to set the that for default all beans are lazy loaded. I know that I can add the #Lazy to all my #Component beans but I want that for default all beans are setted at lazy...
In Spring Boot I don't have an XML configuration file or a configuration class but I only have an application.properties configuration file.
So, how can I set that the default behavior for all the bean is lazy=true
To implement a BeanFactoryPostProcessor that sets lazy initialization by default (which can be required if you are e.g. defining some of the beans dynamically, outside of your #Configuration class(es)), the following approach worked for me:
#Component
public class LazyBeansFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory( ConfigurableListableBeanFactory beanFactory ) throws BeansException {
for ( String name : beanFactory.getBeanDefinitionNames() ) {
beanFactory.getBeanDefinition( name ).setLazyInit( true );
}
}
}
This essentially puts the #Lazy annotation on all your #Component and #Services. You might want to invent a mechanism to annotate classes with #Eager if you go this route, or just hardwire a list in the LazyBeansFactoryPostProcessor above.
Further reading
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/BeanFactoryPostProcessor.html
Since the version 2.2.2.RELEASE of spring-boot you can use the property below in your application.properties file
spring.main.lazy-initialization=true
for further reading and a good example please refer to
https://www.baeldung.com/spring-boot-lazy-initialization
https://spring.io/blog/2019/03/14/lazy-initialization-in-spring-boot-2-2

Spring Boot Application with dependency having multiple datasources

I am trying to create a Spring Boot Application, with a dependency jar which has got context.xml configured with multiple datasources.
In My spring boot application, I added #ImportResource("context.xml") to the #Configuration class and now, I get an exception that
"No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 4: XXXDataSource,YYYDataSource,ZZZDataSource,aaaaDataSource".
I read the documentation on multiple datasources in Spring Boot, but unable to fix this issue. Not sure, how I can configure my class, as I cannot change the dependency jar to change the way datasources are configured.
Please help!
You can use the "Primary" attribute on your datasource bean to make your autowiring choose it by default.
<bean primary="true|false"/>
If you are using Java configuration, use the #Primary annotation instead.
http://docs.spring.io/spring-framework/docs/4.0.4.RELEASE/javadoc-api/org/springframework/context/annotation/Primary.html
#Component
public class FooService {
private FooRepository fooRepository;
#Autowired
public FooService(FooRepository fooRepository) {
this.fooRepository = fooRepository;
}
}
#Component
public class JdbcFooRepository {
public JdbcFooService(DataSource dataSource) {
// ...
}
}
#Primary
#Component
public class HibernateFooRepository {
public HibernateFooService(SessionFactory sessionFactory) {
// ...
}
}
If this still doesn't resolve the issue, you can name the bean, and use the #Qualifier annotation in your java classes, or use the "ref" attribute in your Spring XML configuration.
https://spring.io/blog/2014/11/04/a-quality-qualifier
#Autowired
#Qualifier( "ios") // the use is unique to Spring. It's darned convenient, too!
private MarketPlace marketPlace ;
If you require one of the datasources in the jar and are unable to modify the configuration, rather than importing the xml from the jar, copy the configurations you need into your own local spring context configuration.

Spring Boot - bean definition

I'm looking into using Spring Boot for a new application but I'm having trouble figuring out the best approach to create the application beans.
At a high-level, this would be a web application that can have one or more beans of the same type - each with different property values. If I need to add a new bean of the same type, I should only have to configure it. Typically, if I was using Spring MVC, I would just define each bean in application context and load in the property values via a context file. Spring Boot prefers to do away with xml config, but I'm not sure how to translate the bean definitions into a Spring Boot solution. How do I still take advantage of IoC using Spring Boot.
Actually this has nothing to do with Spring Boot. As you mentioned, it supports both Java and XML bean configurations.
You can easily create multiple beans out of the same class using Java configuration.
XML config like:
<bean id="first" class="com.foo.MyClass" />
<bean id="second" class="com.foo.MyClass" />
translates into:
#Configuration
class MyConfiguration {
#Bean
MyClass first() {
return new MyClass();
}
#Bean
MyClass second() {
return new MyClass();
}
}
with Maciej Walkowiak's answer, it is also recomended to write it like this:
#Configuration
class MyConfiguration {
#Bean
#Qualifier("first")
MyClass first() {
return new MyClass();
}
#Bean
#Qualifier("second")
MyClass second() {
return new MyClass();
}
}
then later when you autowire you can use:
#Autowired
#Qualifier("second")
private MyClass myClass;

Spring create bean only if condition is met

Assume there are two implementations of a single interface, and these beans are declared as beans in the spring configuration xml. Now, I would need only one implementation of the interface based on the system property. And, I don't wanna create the second implementation of the bean. How can I do this? I looked at this blog but then below snippet of the code from this blog uses "new" operate to create the beans. In my case the beans are declared in the spring configuration file.
http://www.intertech.com/Blog/spring-4-conditional-bean-configuration/
#CONFIGURATION
PUBLIC CLASS MYCONFIGURATION {
#BEAN(NAME="EMAILERSERVICE")
#CONDITIONAL(WINDOWSCONDITION.CLASS)
PUBLIC EMAILSERVICE WINDOWSEMAILERSERVICE(){
RETURN NEW WINDOWSEMAILSERVICE();
}
#BEAN(NAME="EMAILERSERVICE")
#CONDITIONAL(LINUXCONDITION.CLASS)
PUBLIC EMAILSERVICE LINUXEMAILERSERVICE(){
RETURN NEW LINUXEMAILSERVICE();
}

Resources