Tuesday, March 28, 2017

Spring Configuration -- Do You Use a Component Scan?

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