Overriding configuration is spring - java

Here is my situation. I have a parent project which has a bean configuration as follows
#Configuration
public class Configuration {
#Bean
public BeanA beanA(#Autowired BeanB beanB) {
return new BeanA(beanB);
}
I want to override this configuration, because I need to override some of the definitions on BeanB.
#Configuration
public class Configuration {
#Bean
public BeanA beanA(#Autowired BeanC beanC) {
return new BeanA(beanC);
}
Where my bean of type C
public class BeanC extends BeanB { ... }
But when I run the application, I am always getting the configuration coming from the parent. Also have enabled the bean-definition-overriding
spring.main.allow-bean-definition-overriding=true
Does anyone knows how can I tell the spring container to use my bean definition instead the one that is coming from my parent project.
Thanks in advance!

You can annotate your bean with #Primary annotation (see: https://www.baeldung.com/spring-primary)
#Configuration
public class Configuration {
#Primary
#Bean
public BeanA beanA(#Autowired BeanC beanC) {
return new BeanA(beanC);
}

Related

spring Bean Overriding 2.1.x in nested class #Configuration #Bean creation fails with 'A bean with that name has already been defined'

upgrading to spring-boot 2.1.0.M4 Bean overriding has been disabled by default. If you are relying on overriding, you will need to set spring.main.allow-bean-definition-overriding to true.
But why is a Bean defined in an inner class also treated like a duplicate bean definition. Is this a bug? or has this some explanation?
e.g.:
#Configuration
public class BusinessLogicConfig {
#Configuration
class BusinessLogicSourceConfig {
#Bean
public BusinessLogic businessLogic() {
return new BusinessLogic("source");
}
}
}
works fine up to spring-boot 2.0.x
but when using e.g. spring-boot 2.1.0.M4 it gives me an error on startup:
The bean 'businessLogic', defined in class path resource [com/example/di/bootconfigs/BusinessLogicConfig$BusinessLogicTier1Config.class], could not be registered.
A bean with that name has already been defined in URL [jar:file:/.../di/build/libs/di-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/com/example/di/businesslogic/BusinessLogic.class]
and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
As I clearly only have a single definition of the bean (only defined in an inner #Configuration class) this seems like a bug to me.
If you ask why I am using a nested inner #Configuration class:
I often have demo code, demonstrating distributed system behavior in which I need more than one app to demonstrate things. As I don't want to have multiple App code, I just start the same App with different profiles and the profile is injecting "different business logic" which I want to "keep together" in one file, like:
#Configuration
public class BusinessLogicConfig {
#Value("${app.info.instance_index}")
private String instanceIndex;
#Profile({ "source" }) // unused fake BusinessLogic for pure sources
#Configuration
class BusinessLogicSourceConfig {
#Bean
public BusinessLogic businessLogic() {
return new BusinessLogic("source", instanceIndex);
}
}
#Profile({ "sink" }) // unused fake BusinessLogic for pure sinks
#Configuration
class BusinessLogicSinkConfig {
#Bean
public BusinessLogic businessLogic() {
return new BusinessLogic("sink", instanceIndex);
}
}
#Profile({ "tier1" })
#Configuration
class BusinessLogicTier1Config {
#Bean
public BusinessLogic businessLogic() {
return new BusinessLogic("tier1", instanceIndex);
}
}
#Profile({ "tier2" })
#Configuration
class BusinessLogicTier2Config {
#Bean
public BusinessLogic businessLogic() {
return new BusinessLogic("tier2", instanceIndex);
}
}
#Profile({ "tier3" })
#Configuration
class BusinessLogicTier3Config {
#Bean
public BusinessLogic businessLogic() {
return new BusinessLogic("tier3", instanceIndex);
}
}
}
Turns out its not the #Configuration class nesting.
It is the BusinessLogic class itself, that is a Bean with name BusinessLogic as it is a #Component. so above creates a second version of a Bean named BusinessLogic.

Injecting library class as dependencies in spring project

I have multiple library classes in my project which need to be injected into a service class. This is the error statement for IntegrationFactory class:
Consider defining a bean of type 'com.ignitionone.service.programmanager.integration.IntegrationFactory' in your configuration.
This error is coming on almost every injection where this library class is injected.
I have already added the Library package in #ComponentScan, but, as it is read-only file, I can not annotate the library class. I came to know from some answer here that Spring can not inject classes which it does not manage. This library is not built on spring.
I have tried to create a #Bean method which returns the IntegrationFactory(class in question) in the class where #Inject is used, but this too does not seem to work.
How can this be done, preferably without creating a stub/copy class?
This is EngagementServiceImpl class snippet:
#Inject
public EngagementServiceImpl(EngagementRepository engagementRepository,
#Lazy IntegrationFactory integrationFactory, TokenRepository tokenRepository,
EngagementPartnerRepository engagementPartnerRepository, MetricsService metricsService) {
this.engagementRepository = engagementRepository;
this.integrationFactory = integrationFactory;
this.tokenRepository = tokenRepository;
this.engagementPartnerRepository = engagementPartnerRepository;
this.metricsService = metricsService;
}
This is injection part:
#Autowired
private EngagementService engagementService;
This is ConfigClass:
#Configuration
public class ConfigClass {
#Bean
public IntegrationFactory getIntegrationFactory(){
Map<String, Object> globalConfig = new HashMap<>();
return new IntegrationFactory(globalConfig);
}
#Bean
#Primary
public EntityDataStore getEntityDataStore(){
EntityModel entityModel = Models.ENTITY;
return new EntityDataStore(this.dataSource(), entityModel );
}
#ConfigurationProperties(prefix = "datasource.postgres")
#Bean
#Primary
public DataSource dataSource() {
return DataSourceBuilder
.create()
.build();
}
}
You need to add your bean definitions in a configuration class.
#Configuration
public class ServiceConfig {
#Bean
public IntegrationFactory getIntegrationFactory(){
// return an IntegrationFactory instance
}
}
Then you have to make sure your #Configuration class gets detected by Spring, either by having it within your scanned path or by manually importing it via #Import from somewhere withing you scanned path. An example of #Import, considering you are using Spring Boot.
#Import(ServiceConfig.class)
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Hope this helps!
Your Bean IntegrationFactory can't be found, as it is not annotated with any Spring stereotype and therefore not recognized by the component scan.
As you have multiple options to provide an instance of your class to the application context, read the Spring documentation (which also includes samples) to find out which one fits you the most:
https://docs.spring.io/spring/docs/5.1.0.RELEASE/spring-framework-reference/core.html#beans-java-basic-concepts
One Option would be to create a factory which provides an instance of your class to the application context, like it is stated in the documentation:
#Configuration
public class AppConfig {
#Bean
public IntegrationFactory myIntegrationFactory() {
return new IntegrationFactory();
}
}
Do not forget to add the Configuration to the application context.

Spring Boot with more context

I have a spring boot with the main class with #SpringBootApplication (so it is has implied tags #EnableAutoConfiguration, #ComponentScan, and #Configuration).
What happens if I create another class with annotation #Configuration and #ComponentScan? Do I create another container of beans? In this way the beans are duplicates? Is a good way create more #Configuration class? #Configuration create a container of beans? If yes the two containers share the bean?
I need to understand these question.
What happens if i create another class with annotation #Configuration and #Component Scan?
that is ok, is perfectly normal..
I create another container of beans ? In this way the beans are Duplicates ?
If you create two beans of the same type, you will have an error when the app is starting.. you need to declare one of them as #Primary
you can define multiple #configuration in spring boot as
Matias Elorriaga mentioned.
#Configuration equals to <beans> tag in spring xml which hold many <bean> (equal to #Bean) inside of this, same a class with annotation #Configuration say hey you can define multiple beans inside me
e.f:
#Configuration
public class AppConfig {
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("message");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public Validator validator() {
final LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
localValidatorFactoryBean.setValidationMessageSource(messageSource());
return localValidatorFactoryBean;
}
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
sessionLocaleResolver.setDefaultLocale(Locale.ENGLISH);
return sessionLocaleResolver;
}
}
Also if you have 2 different class with same type of Bean
e.g. class: SpringConfigA have Composite bean
#Configuration
public class SpringConfigA {
#Bean
public Composite composite() {
Composite c = new Composite();
return c;
}
}
and another class SpringConfigB have same Composite bean
#Configuration
public class SpringConfigB {
#Bean
public Composite composite() {
Composite c = new Composite();
return c;
}
}
then it's will throw the exception of during bean initialization because of ambiguity with Composite beans so
here you can use Qualifier to fixed this issue
so
#Configuration
public class SpringConfigA {
#Bean
#Qualifier("compositeA")
public Composite composite() {
Composite c = new Composite();
return c;
}
}
and another class SpringConfigB have same Composite bean with Qualifier name "compositeB"
#Configuration
public class SpringConfigB {
#Bean
#Qualifier("compositeB")
public Composite composite() {
Composite c = new Composite();
return c;
}
}
yes you can register a bean using #ComponentScan by either registering configuration bean in dispatcher servlet or by importing a seperate configuration bean which is lready registered in spring container.
suppose you have Config class in which you are scanning component-
#ComponentScan(basePackages = {"xyz"})
#Configuration
public class Config {
....
}
To register Config in container you must do-
new AnnotationConfigWebApplicationContext().register(Config .class);
Or
#Configuration
#Import({MvcConfig.class})
public class AnotherConfig {
....
}

Spring #Profile works on class, but not on #Bean

I'm unable to get #Profile to work on a #Bean, but it works fine on the #Configuration class. In my module tests I correctly get the MyServiceStub autowired in the first example, but not in the second.
Does anyone know why?
This works:
MyConfig.java
#Configuration
#Import({StubConfig.class, RealConfig.class}
public class MyConfig {}
StubConfig.java
#Profile({"featureTest", "moduleTest"})
#Configuration
public class StubConfig {
#Bean
public MyService myService() {
return new MyServiceStub();
}
}
RealConfig.java
#Profile({"!featureTest", "!moduleTest"})
#Configuration
public class RealConfig {
#Bean
public MyService myService() {
return new MyService();
}
}
But this doesn't work:
MyConfig.java
#Configuration
public class MyConfig {
#Bean
#Profile({"featureTest", "moduleTest"})
public MyService myServiceStub() {
return new MyServiceStub();
}
#Bean
#Profile({"!featureTest", "!moduleTest"})
public MyService myService() {
return new MyService();
}
}
Instead of using #Profile approach, you could try using the #Conditional to have conditional bean creation.
Or have an explicit if-stmt to check what is the activeProfile. And you can create the Bean instance depending on the activeProfile. This approach I think would mirror what could be done in Spring XML using their expression language. But the #Conditional seems better fit for you.

How do I express a dependency on a bean defined in an imported configuration in Spring?

I recently started working at a place that uses Java configuration for Spring as opposed to XML and so far I'm loving it.
My question is the following:
If we have a #Configuration annotated class A that imports another #Configuration annotated class B, what is the proper, type-safe way for a bean defined in A to depend on a bean defined in B.
Here's an example I saw in a blog (https://blog.codecentric.de/en/2012/07/spring-dependency-injection-styles-why-i-love-java-based-configuration/):
#Configuration
public class PartnerConfig {
#Bean
public PartnerService partnerService() {
return new PartnerServiceImpl();
}
}
#Configuration
#Import(PartnerConfig.class)
public class CashingConfig {
#Autowired
private PartnerConfig partnerConfig;
#Bean
public CashingService cashingService() {
return new CashingServiceImpl(partnerConfig.partnerService());
}
}
As a second part to my question, if I was to do the above, would Spring interpret as a bean dependency? That is, when I do
partnerConfig.partnerService()
in the example above, am I getting Spring to fetch me the partnerService bean, or am I just calling a regular java method and creating a new instance of the PartherService (which is NOT what I want, since the bean should be a singleton) ?
EDIT:
It has been suggested to use a #Qualifier. Would this work?
#Configuration
public class PartnerConfig {
#Bean
#MyCustomQualifier
public PartnerService partnerService() {
return new PartnerServiceImpl();
}
}
#Configuration
#Import(PartnerConfig.class)
public class CashingConfig {
#Bean
public CashingService cashingService(#MyCustomQualifier PartnerService partnerService) {
return new CashingServiceImpl(partnerService);
}
}
I recommend giving the docs a read: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html
Refer to the section:
#Bean Methods in #Configuration Classes
This sums it up very well.
Typically, #Bean methods are declared within #Configuration classes. In this case, bean methods may reference other #Bean methods in the same class by calling them directly. This ensures that references between beans are strongly typed and navigable.
Also take a look at: http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html
Section:
Composing #Configuration classes
Just add the dependency as an argument to the #Bean annotated method and remove the autowiring of the configuration.
#Configuration
#Import(PartnerConfig.class)
public class CashingConfig {
#Bean
public CashingService cashingService(PartnerService partnerService) {
return new CashingServiceImpl(partnerService);
}
}
or simply autowire the PartnerService instead of the configuration.
#Configuration
#Import(PartnerConfig.class)
public class CashingConfig {
#Autowire
private PartnerService partnerService;
#Bean
public CashingService cashingService() {
return new CashingServiceImpl(partnerService);
}
}

Categories