If you use Spring beans for dependency injection in any of your applications then you know that you frequently have to define quite a lot of them. Whether you use xml configuration, Java annotation-based configuration, or a mix of the two, you still wind up creating a bean for each component of your application. With even a moderately complex system, this number of beans adds up very quickly. Thus, we have the component scan.
For those of you who aren't familiar with it, a component scan makes it very easy to create a new component. All you need to do is place an @Component tag on the class itself and then indicate in one of your configuration files that Spring should scan for components in the directory that your class is in.
A brief example:
First, define the component using the @Component tag.
[pre class="brush:java" title="MyComponent.java"]package my.component.pkg;
import org.springframework.stereotype.Component;
@Component
public class MyComponent {
...
}[/pre]
Next, tell Spring to scan for components. I'll give both the Java and xml configurations, but really only one is necessary.
[pre class="brush:java" title="MyConfiguration.java"]package my.configuration.pkg;
import my.component.pkg.MyComponent;
import org.springframework.context.annotation.*;
@Configuration
@ComponentScan("my.component.pkg")
public class MyConfiguration {
}[/pre]
[pre class="brush:xml" title="component-configuration.xml"]<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.7.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.7.xsd">
<context:component-scan base-package="my.component.pkg" />
</beans>[/pre]
Pretty nifty, huh? Well, maybe. Until you try to run a bunch of integration tests. You see, I might have hundreds (or even thousands) of components defined this way (within the same package or in multiple base packages). Now suppose I want to run an integration test on MyComponent. When Spring starts up, it loads not just MyComponent, but every component in your application. This is not only unnecessary, but for a very large application, it can take up to a minute, possibly more, before your test can even begin running.
The above issue has led my team recently to abandoning using the component scan. We have begun defining beans using the @Bean tag in a Java configuration file in the same package as the component. The above example would then look like this:
[pre class="brush:java" title="MyComponent.java"]package my.component.pkg;
class MyComponent {
...
}[/pre]
[pre class="brush:java" title="ComponentConfig.java"]package my.component.pkg;
import org.springframework.context.annotation.*;
@Configuration
public class ComponentConfig {
@Bean
MyComponent myComponent() {
return new MyComponent();
}
..
}[/pre]
We would define all components in the my.component.pkg package in this configuration file. Then, when running a test on MyComponent, I only need to load this configuration and can skip loading the components that are not needed for the test.
This finally brings me to my question: do you/would you use component scan to simplify the creation of component beans or do you/would you avoid it in order to be able to more easily control how much of the application context must be loaded when running a test? Please leave a response in the comments.
No comments:
Post a Comment