«    »

Java-Based Configuration of Spring Dependency Injection

At the heart of the Spring Framework is its dependency injection capabilities provided by its inversion-of-control container. Traditionally the configuration of dependencies has been done in one or more separate XML files. In these files you need to specify code-specific constructs such as the concrete classes to use as implementations of interfaces. It has always seemed unnatural to me to do this configuration within XML instead of using Java code. One problem, for example, is that doing renames of your classes or packages will break your XML configuration. Tooling support for these Spring XML files has admittedly helped, but doesn't overcome for me the fundamental inconsistency in using XML to configure Java code. What's wrong with using Java? Traditionally, one significant problem with doing Spring configuration within Java code is that it turned out to be much more verbose than the XML files – the Spring Java APIs were not explicitly designed and optimized to do Java-based configuration.

This state of affairs with Spring configuration changed with the introduction of the Spring Java Configuration project, commonly called JavaConfig. JavaConfig provides roughly the same capabilities as Spring's XML-based configuration but uses Java code that is quite compact and readable. I have tried out JavaConfig and I really like it – my unease over XML configuration has been finally laid to rest. Using JavaConfig is fairly simple: you create one or more configuration classes using the conventions expected by JavaConfig, then create an instance of JavaConfigApplicationContext or JavaConfigWebApplicationContext and use it like a regular Spring AppplicationContext.

The most significant decision I think you need to make when using JavaConfig is what approach to use to define dependencies. The approach you choose determines how you go about writing your configuration class. While this decision is not specific to JavaConfig - Spring's standard XML configuration allows for the same set of approaches to be used – the application of each approach using JavaConfig is different. These three approaches to defining dependencies are:

  1. Explicit declaration of beans and dependencies.
  2. Automatic dependencies via autowiring.
  3. Automatic beans via component scanning.

In the remainder of this article I am going to provide examples using JavaConfig for these three approaches and discuss the pros and cons of each. While the examples focus exclusively on one of the approaches at a time you can easily combine all three approaches within a single configuration class. For the sake of clarity, however, I recommend on real projects to choose one approach as the primary method of configuration. The source code listed in this article is provided in the Java Examples project which can be downloaded from the Software page.

All three approaches use the same configuration example. There are two beans to configure, each with an interface and an underlying implementation class. The first bean is ResumeRepository which is a Repository class (aka Data Access Object) that provides CRUD-type operations for the domain object Resume. For this example this repository interface only contains a single method Set<Resume> findAll(). The second bean is CalculatorService, which is a Service class that provides functionality relating to resumes: for this example a single method void validate(Resume). The Java code for these two interfaces and the domain object is provided below:

package com.basilv.examples.spring;
import java.util.Set;

public interface CalculatorService
{
  boolean validate(Resume resume);
}

public interface ResumeRepository
{
  Set<Resume> findAll();
}

public class Resume
{
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  
  private String name; 
}

The implementation class of CalculatorService is CalculatorServiceImpl which requires (has a dependency on) ResumeRepository. The implementation class of ResumeRepository is HardcodedResumeRepository, which is used for this example rather than a more typical repository implementation to avoid further dependencies on e.g. a DataSource. These implementation classes are the same in terms of logic across the three approaches, but there are configuration differences so the code for these classes will be shown for each approach below.

Explicit Declaration of Beans and Dependencies

The explicit approach requires that all beans and their dependencies be explicitly coded within the configuration class as shown below:

package com.basilv.examples.spring.explicit;

import org.springframework.config.java.annotation.*;
import com.basilv.examples.spring.*;

@Configuration
public class ExplicitConfig
{
  @Bean
  public ResumeRepository resumeRepository() {
    return new HardcodedResumeRepository();
  }

  @Bean
  public CalculatorService calculatorService() {
    return new CalculatorServiceImpl(
      resumeRepository()); // Inject dependency
  }
}

The @Configuration annotation is needed on the class, and each bean definition method must be annotated with @Bean. By default bean names are based on the method names.

The implementation classes HardcodedResumeRepository and CalculatorServiceImpl do not require any special configuration code. Constructor injection was used in CalculatorServiceImpl as it is safer and results in more compact code within the configuration class. The code for these implementation classes is shown below:

package com.basilv.examples.spring.explicit;

import java.util.*;
import com.basilv.examples.spring.*;

public class HardcodedResumeRepository implements ResumeRepository
{
  public Set<Resume> findAll() {
    HashSet<Resume> set = new HashSet<Resume>();
    Resume resume = new Resume();
    resume.setName("Coded, Hard");
    set.add(resume);
    
    return set;
  }
}

public class CalculatorServiceImpl implements CalculatorService
{
  private ResumeRepository resumeRepository;
  
  public CalculatorServiceImpl(ResumeRepository resumeRepository) {
    this.resumeRepository = resumeRepository;
  }

  public boolean validate(Resume resume) {
      for (Resume repoResume : resumeRepository.findAll()) {
        if (repoResume.getName().equals(resume.getName())) {
          return true;
        }
      }
      
      return false;
  }
}

As explained above, using the ExplicitConfig configuration class is quite simple. The unit test shown below demonstrates how it is done.

package com.basilv.examples.spring.explicit;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.util.Set;

import org.junit.*;
import org.springframework.config.java.context.JavaConfigApplicationContext;

import com.basilv.examples.spring.*;

public class ExplicitConfigTest
{
  private JavaConfigApplicationContext context;

  @Before
  public void setUp() {
    context = new JavaConfigApplicationContext(ExplicitConfig.class);
  }

  @Test
  public void repositoryConfig() {

    ResumeRepository resumeRepository = context.getBean(ResumeRepository.class);
    Set<Resume> resumes = resumeRepository.findAll();
    assertNotNull(resumes);
    assertTrue(!resumes.isEmpty());
  }

  @Test
  public void serviceConfig() {

    CalculatorService service = context.getBean(CalculatorService.class);
    assertNotNull(service);

    assertFalse(service.validate(new Resume()));
  }

}

One nice feature of the JavaConfigApplicationContext class is that beans can be retrieved via the getBean method in a type-safe manner with no casting required. The only piece of this code that is specific to this approach is the configuration class used – the other approaches to defining dependencies do not need to change anything else in this test case.

At first glance it may appear that the configuration class with its explicit definitions could be used directly without Spring. That would be correct for this simplistic example. In real-life usage, however, the Spring Framework provides many additional features via its container including bean life-cycle events, bean scope (singleton versus prototype), and aspects such as transactional behavior.

Automatic Dependencies via Autowiring

While the explicit declaration approach described above may look quite reasonable for this simplistic example, in real-life usage the number of dependencies per class can really add up, particularly for Service classes that may require five or more Repository or other Service classes. Instead of manually defining all of these dependencies, Spring allows such dependencies to be inferred using a feature called Autowiring.

Here is how Autowiring works. Dependencies within an implementation class must be flagged with the @Autowired annotation. At bean construction time the Spring container will automatically populate these dependencies by finding a matching bean based on the Java type. (Multiple beans of the same type can be handled through the use of qualifiers or through the @Resource annotation. See the Spring documentation for further details.)

While the Spring XML-based configuration can autowire both constructor and setter injection, Java-based configuration cannot autowire dependencies in constructors when beans are explicitly defined. So for the example the CalculatorServiceImpl class had to be changed to use setter injection. Below is the code for the configuration class and CalculatorServiceImpl - the HardcodedResumeRepository class is unchanged from the explicit declaration approach.

package com.basilv.examples.spring.autowire;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.config.java.annotation.*;
import org.springframework.config.java.plugin.context.AnnotationDrivenConfig;
import com.basilv.examples.spring.*;

@Configuration
@AnnotationDrivenConfig // Needed for @Autowire to work
public class AutowireConfig
{

  @Bean
  public ResumeRepository resumeRepository() {
    return new HardcodedResumeRepository();
  }

  @Bean
  public CalculatorService calculatorService() {
    return new CalculatorServiceImpl(); // Do not need to specify dependency.
  }
}

public class CalculatorServiceImpl implements CalculatorService
{
  @Autowired
  private ResumeRepository resumeRepository;
  

  public boolean validate(Resume resume) {
      for (Resume repoResume : resumeRepository.findAll()) {
        if (repoResume.getName().equals(resume.getName())) {
          return true;
        }
      }
      
      return false;
  }

  public void setResumeRepository(ResumeRepository resumeRepository) {
    this.resumeRepository = resumeRepository;
  }
}

Automatic Beans via Component Scanning

While the previous approach did reduce the amount of configuration required, each bean still needed to be explicitly defined within the configuration class. This approach eliminates this requirement by enabling Spring to scan for component classes that are automatically configured as beans with dependencies autowired as per the previous approach. Bean implementation classes must be annotated with the annotation @Component or a specialization of this annotation such as @Service or @Repository. The configuration class is then configured to scan one or more packages for these classes. The code below shows the configuration class and the two implementation classes which were revised to add these annotations.

package com.basilv.examples.spring.componentscan;

import java.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.config.java.annotation.Configuration;
import org.springframework.config.java.plugin.context.*;
import org.springframework.stereotype.*;
import com.basilv.examples.spring.*;

@Configuration
@AnnotationDrivenConfig // Needed for @Autowire to work
@ComponentScan("com.basilv.examples.spring.componentscan") 
public class ComponentScanConfig
{
  // No explicit configuration needed
}

@Repository
public class HardcodedResumeRepository implements ResumeRepository
{
  public Set<Resume> findAll() {
    HashSet<Resume> set = new HashSet<Resume>();
    Resume resume = new Resume();
    resume.setName("Coded, Hard");
    set.add(resume);
    
    return set;
  }
}

@Service
public class CalculatorServiceImpl implements CalculatorService
{
  private ResumeRepository resumeRepository;

  @Autowired
  public CalculatorServiceImpl(ResumeRepository resumeRepository) {
    super();
    this.resumeRepository = resumeRepository;
  }
  
  public boolean validate(Resume resume) {
      for (Resume repoResume : resumeRepository.findAll()) {
        if (repoResume.getName().equals(resume.getName())) {
          return true;
        }
      }
      
      return false;
  }
}

The most notable thing about this example is that the configuration class is empty – all bean definitions and dependencies have been inferred via annotations. The CalculatorServiceImpl class is also able to use autowired constructor injection, unlike the previous approach, because the class itself is being instantiated by the Spring container rather than explicitly in our configuration class.

Pros and Cons

There are advantages and disadvantages to each configuration approach. The explicit declaration approach is the easiest to understand, especially for developers unfamiliar with Spring. But this approach is the most work to set up. The approach of autowiring dependencies while explicitly defining beans strikes I believe a nice balance between these two factors. Implementation classes are still explicitly referenced, which I think is important for developers unfamiliar with Spring as otherwise IDEs will report no uses of such classes within the production code base. But all dependency information can be left out. A significant disadvantage with this approach, however, is that constructor injection cannot be autowired. In general I tend to prefer constructor injection, which makes this approach pointless (as constructor dependencies still need to be explicitly defined). The third approach of using component scanning is the most aggressive in minimizing configuration needed – virtually no setup work is required. But I am concerned that this will be much harder to understand for those developers unfamiliar with Spring as a lot of magic is being performed behind the scenes.

Is there a clear winner? I would say no, which is one reason Spring provides for these various configuration options. While I value the ease-of-understanding provided by the first approach, I strongly dislike writing "boilerplate" code, which much of the dependency configuration is, and this pushes me towards the third approach. As per the comment below from Chris Beams, Lead for the Spring JavaConfig project, his recommendation is to use component scanning and not use the hybrid explicit beans + autowiring approach. He also points out that 'external' dependencies, such as data sources, still require explicit configuration.

Despite the questions over the approach to defining dependencies, I am happy with the use of Spring JavaConfig overall. One issue I encountered using Spring JavaConfig is that it is still in beta – it has not had a final 1.0 release yet. I used JavaConfig 1.0.0.M4, which worked fine with Spring 2.5.6 but does not work with Spring 3.0.M2 due to classpath issues. I discovered on the Spring forums that some portion of the JavaConfig functionality with be included in the Spring 3.0 release due out this summer, but it is unclear what is planned for the remaining functionality. See Chris Beam's comment below for more details.

If you find this article helpful, please make a donation.

4 Comments on “Java-Based Configuration of Spring Dependency Injection”

  1. Chris Beams says:

    Hi Basil,

    A nice write up, and I’m glad to hear you like what you’re seeing. I thought I’d take a moment to address your questions and concerns:

    > Explicit bean declaration and DI vs. full component-scanning, vs. a mix of bean declaration and dependency autowiring:

    I’ll just speak from the experience that I and other SpringSource consultants have seen with real teams using Spring ‘in the wild’. While explicit XML-based configuration is still very common, a large percentage of teams have taken to using component scanning liberally throughout their applications. This does require a bit of educating the team as to how it works, but what doesn’t? The productivity benefits gained from not needing to return to the XML for every change seem to outweigh the initial and very short learning curve. Making proper use of the @Service, @Repository annotations (as you did above) helps in this process. The team gets to know what these annotations mean rather quickly. I wouldn’t underestimate developers too much.

    I would generally advise against the third option you mention, of defining the bean definition explicitly, but allowing dependencies to be autowired. This is a kind of ‘one foot in, one foot out’ approach, and comes off a little bit like false advertising. I find it the most confusing personally, and have heard the same sentiments in teams I’ve consulted with. I have actually never seen a serious application use this mix of explicit declaration and autowiring as a team standard.

    Now, keep in mind that even if one agressively adopts component scanning and use of @Autowired, there is still, by necessity, a place for explicit bean declaration and wiring. Any time you’re dealing with a third party class (usually infrastructure-related, such as a DataSource), you of course must declare this explicitly in XML (or JavaConfig).

    This sums up the best practice that I see with teams using Spring in a modern fashion: Explicit wiring for infrastructure (JMS, JDBC, etc), and component-scan/@Autowired for the service and data access layers.

    > Component-scanning issues in Java EE application servers:

    Spring’s component scanning facility is pretty solid. I know of no open issues around classloading problems in Java EE environments. Using component scanning in an OSGi environment is more tricky, but even those problems have all been addressed in Spring DM and dm Server.

    > Future of JavaConfig

    As you mentioned, essential JavaConfig functionality has been ported to Spring Core for the 3.0M3 release. Although 3.0M3 has not yet dropped, the current phase of the porting effort is complete. I’d love it if you’d take a look and provide feedback on what you see.

    You can grab the latest nightly build snapshot of Spring 3.0 using Maven: http://pastebin.com/f472b1c7d. Explore the org.springframework.context.annotation package to see what’s there / what’s new. You’ll see the familiar @Configuration, @Bean and @Import annotations; also you’ll see that @Scope has been updated for use with @Bean methods, and there are also now @Lazy, @Primary and @DependsOn annotations that are usable with @Bean methods or at the class level in conjunction with @Component.

    You’ll notice that certain annotations and classes have not been ported, such as JavaConfig[Web]ApplicationContext, @ComponentScan, @AnnotationDrivenConfig and others.

    Right now, in an effort to keep things as minimal as possible, the ‘bootstrapping mechanism’ for JavaConfig is XML. You can have a very minimal XML file such as this one: http://pastebin.com/f76cd1c59

    There is an existing issue against Spring 3 to port the JavaConfig ApplicationContext implementations. You may want to watch/vote/comment on that issue to stay tuned: http://jira.springframework.org/browse/SPR-5682

    > Compatibility issues between JavaConfig 1.0.0.M4 and Spring 3.0M2

    Note that these issues have been worked out in the forthcoming Spring 3.0M3, so it’s actually possible now to us JavaConfig 1.0.0.M4 against the nightly snapshots mentioned above. This usage is mutually exclusive with the new functionality that’s been ported into core, but you can use one or the other per your choice.

    Again, all feedback is welcome, and thanks for your interest!

    Chris Beams
    Lead, Spring JavaConfig
    http://springsource.org/javaconfig
    twitter: @cbeams, @javaconfig

  2. Thanks Chris for the great comment. I’ve updated the post to change the recommended approach to component-scanning. Your work on JavaConfig is much appreciated.

  3. [...] Java-Based Configuration of Spring Dependency Injection (basilv.com) [...]

  4. [...] 1. Spring Batch or How Not to Design an API 2. Java-Based Configuration of Spring Dependency Injection 3. Spring Batch user guide Share this:TwitterFacebookLinkedInEmailPrintLike this:LikeBe the first [...]

Leave a Reply

(Not displayed)

«    »