Reputation: 4327
I'm having two spring(4.2) java configurations, one in a base module and one in a client specific module:
@Configuration
public class BaseConfig {
@Bean
public A getA() {
return new A("aaa");
}
}
@Configuration
public class ClientConfig {
@Bean
public A getA() {
return new A("bbbb");
}
}
During the app load there is always BaseConfig.getA()
called, how can I ovverride the base bean factory configuration to have some client specific stuff?
Upvotes: 5
Views: 3669
Reputation: 5035
This is an answer to a comment above, but since comments have limited formating and size, I will reply with an answer instead.
how does spring define the ordering and overriding of beans when loading configuration files
It depends what you mean by loading multiple configuration. If you have one spring context and have two classes with @Configuration and do a component scan, then Spring will build the dependency tree, and which ever context (bean) is loaded last will define the bean (as it overrides the fist definition).
If you have have multiple Spring contexts in a parent child relation, then the child and see parent beans, and will also 'override' parent beans if you use child.getBean(type.class). The parent can't see bean from children.
Using @Primary. If you have a Spring context (can come from multiple configurations) that defines two beans of the same type, you will not be able to use context.getBean(type.class) or @AutoWired (without @Qualifier) because you have multiple beans of the same type. This behaviour can be altered if one of the beans is @Primary. I try to avoid the use of @Primary in my own code, but I can see it is used heavily used in Spring boots auto configure system, so I think it has some subtle usage when it comes to framework design.
Here is a small example, note that if you load configuration classes directly, they don't need to have the @Configuration annotation.
public class ParentChildContext {
public static void main(String[] args) {
parentChildContext();
twoConfigurationsSameContext();
}
private static void twoConfigurationsSameContext() {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Parent.class, Child.class);
// if you have two beans of the same type in a context they can be loaded by name
Object childA = ctx.getBean("childA");
System.out.println("childA = " + childA);
Object parentA = ctx.getBean("parentA");
System.out.println("parentA = " + parentA);
// since both configurations define A, you can't do this
ctx.getBean(A.class);
}
private static void parentChildContext() {
ApplicationContext parentCtx = new AnnotationConfigApplicationContext(Parent.class);
A parentA = parentCtx.getBean(A.class);
System.out.println("parent = " + parentA);
AnnotationConfigApplicationContext childCtx = new AnnotationConfigApplicationContext();
childCtx.register(Child.class);
childCtx.setParent(parentCtx);
childCtx.refresh();
A childA = childCtx.getBean(A.class);
System.out.println("child = " + childA);
}
public static class Parent {
@Bean
//@Primary // if you enable @Primary parent bean will override child unless the context is hierarchical
public A parentA() {
return new A("parent");
}
}
public static class Child {
@Bean
public A childA() {
return new A("child");
}
}
public static class A {
private final String s;
public A(String s) {
this.s = s;
}
@Override
public String toString() {
return "A{s='" + s + "'}";
}
}
}
Upvotes: 0
Reputation: 13835
@Profile Annotation can be used for this...
@Configuration
@Profile("base")
public class BaseConfig {
@Bean
public A getA() {
return new A("aaa");
}
}
@Configuration
@Profile("client")
public class ClientConfig {
@Bean
public A getA() {
return new A("bbbb");
}
}
Use the following link https://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile/
Upvotes: 0
Reputation: 5035
Personally I would NEVER override a bean in spring! I have seen people spend too much time debugging issues related to this. For the same reason I would never use @Primary.
In this case I would have 3 contexts
This way you will specify the 2 contexts to load. This could be done programatically, or using profiles. Potentially you will need more contexts, because you probably want some of your beans to be different in tests.
Upvotes: 2
Reputation: 2541
I think that you should take a look at the @Profile annotation. You could simply split configuration into different base and client specific one like:
@Configuration
@Profile("base")
public class BaseConfig {
@Bean
public A getA() {
return new A("aaa");
}
}
@Configuration
@Profile("client")
public class ClientConfig {
@Bean
public A getA() {
return new A("bbbb");
}
}
now run the specific profile by adding
Upvotes: 1
Reputation: 1533
If you include both configurations you can check Primary annotation: Primary
Indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one 'primary' bean exists among the candidates, it will be the autowired value.
Upvotes: 0
Reputation: 2035
I'm not sure on how to extend bean config classes. One solution is to mark the bean in ClientConfig
with @Primary annotation. This will cause the ClientConfig definition of bean A to be used.
Upvotes: 0