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.

Friday, March 10, 2017

The final keyword in Java

Most experienced Java developers are familiar with the final keyword. It can be used at three different levels: class, method, and variable (or field).

Probably the most common use of final is in the definition of constants, such as

public static final Integer MY_CONSTANT = 475;


Another common use is for local variables (or method parameters) whose value never changes:

String getNameAndAge() {
    final String name = determineName();
    final Integer age = determineAge();
    return name + " " + age;
}

That the values of name and age in the above example cannot be changed leads some to believe that the final keyword makes an object immutable. In a code review I was doing recently, I came across a snippet of code similar to the following:

// make the context immutable
final Context context = contextFactory.build(contextParameters);

The author had been told in a previous comment to make Context immutable, and this was their attempt to do so. To understand why this fails to make context immutable, it helps to recall how Java stores data.

Briefly, for primitive values, the value is stored directly in the stack. For the sake of visualization, we can say that the variable stores the value itself. For objects, however, the variable stores not the object, but a reference to the object. In other words, adding the final keyword only limits you from reassigning the reference that is stored by the variable.

This means that the final keyword in the above case means that you cannot assign a different Context instance to context, but you can use the setter methods in the Context class to modify the instance that you do have, so you do not have an immutable instance of Context. The way to make Context immutable is not in the use of final, but instead in removing the setter methods from the Context class itself. You cannot make an immutable instance of a class that is itself mutable.

As mentioned above, final can be used at the class level and at the method level as well. When used at the class level, it means that the class cannot be extended (see java.lang.String). When used at the method level, it means that the method cannot be overridden.

There are style issues around final as well, such as whether or not to label method parameters as final or whether final should be used on local variables at all. Style is a bit outside the scope of this article, but I urge you (and your team) to have a consistently used policy.