Reputation: 485
Java configuration allows us to manage bean creation within a configuration file. Annotated @Component
, @Service
classes used with component scanning does the same. However, I'm concerned about using these two mechanisms at the same time.
Should Java configuration and annotated component scans be avoided in the same project? I ask because the result is unclear in the following scenario:
@Configuration
public class MyConfig {
@Bean
public Foo foo() {
return new Foo(500);
}
}
...
@Component
public class Foo {
private int value;
public Foo() {
}
public Foo(int value) {
this.value = value;
}
}
...
public class Consumer {
@Autowired
Foo foo;
...
}
So, in the above situation, will the Consumer get a Foo instance with a 500 value or 0 value? I've tested locally and it appears that the Java configured Foo (with value 500) is created consistently. However, I'm concerned that my testing isn't thorough enough to be conclusive.
What is the real answer? Using both Java config and component scanning on @Component beans of the same type seems like a bad thing.
Upvotes: 5
Views: 5547
Reputation: 21
I think your concern is more like raised by the following use case:
You have a custom spring-starter-library that have its own @Configuration
classes and @Bean
definitions, BUT if you have @Component/@Service
in this library, you will need to explicitly @ComponentScan
these packages from your service, since the default @ComponentScan
(see @SpringBootApplication
) will perform component scanning from the main class, to all sub-packages of your app, BUT not the packages inside the external library. For that purpose, you only need to have @Bean
definitions in your external library, and to inject these external configurations via @EnableSomething
annotation used on your app's main class (using @Import(YourConfigurationAnnotatedClass.class)
OR via using spring.factories
in case you always need the external configuration to be used/injected.
Of course, you CAN have @Components
in this library, but the explicit usage of @ComponentScan
annotation may lead to unintended behaviour in some cases, so I would recommend to avoid that.
So, to answer your question -> You can have both approaches of defining beans, only if they're inside your app, but bean definitions outside your app (e.g. library) should be explicitly defined with @Bean
inside a @Configuration
class.
Upvotes: 2
Reputation: 589
In general, there is nothing wrong with component scanning and explicit bean definitions in the same application context. I tend to use component scanning where possible, and create the few beans that need more setup with @Bean
methods.
There is no upside to include classes in the component scan when you create beans of their type explicitly. Component scanning can easily be targeted at certain classes and packages. If you design your packages accordingly, you can component scan only the packages without "special" bean classes (or else use more advanced filters on scanning).
In a quick look I didn't find any clear information about bean definition precedence in such a case. Typically there is a deterministic and fairly stable order in which these are processed, but if it is not documented it maybe could change in some future Spring version.
Upvotes: 1
Reputation: 2239
It is perfectly valid to have Java configuration and annotated component scans in the same project because they server different purposes.
@Component
(@Service,@Repository
etc) are used to auto-detect and auto-configure beans.
@Bean
annotation is used to explicitly declare a single bean, instead of letting Spring do it automatically.
You can do the following with @Bean
. But, this is not possible with @Component
@Bean
public MyService myService(boolean someCondition) {
if(someCondition) {
return new MyServiceImpl1();
}else{
return new MyServiceImpl2();
}
}
Haven't really faced a situation where both Java config and component scanning on the bean of the same type were required.
As per the spring documentation,
To declare a bean, simply annotate a method with the @Bean annotation. When JavaConfig encounters such a method, it will execute that method and register the return value as a bean within a BeanFactory. By default, the bean name will be the same as the method name
So, As per this, it is returning the correct Foo (with value 500).
Upvotes: 1