4.12 Classpath scanning, managed components and writing configurations using Java

Thus far most of the examples within this chapter have used XML for specifying the configuration metadata that produces each BeanDefinition within the Spring container. The previous section (Section 4.11, “Annotation-based configuration”) demonstrated the possibility of providing a considerable amount of the configuration metadata using source-level annotations. Even in those examples however, the "base" bean definitions were explicitly defined in the XML file while the annotations were driving the dependency injection only. The current section introduces an option for implicitly detecting the candidate components by scanning the classpath and matching against filters.

[Note]Note

Starting with Spring 3.0 many of the features provided by the Spring JavaConfig project have been added to the core Spring Framework. This allows you to define beans using Java rather than using the traditional XML files. Take a look at the @Configuration, @Bean, @Import and @DependsOn annotations for how to use these new features.

4.12.1 @Component and further stereotype annotations

Beginning with Spring 2.0, the @Repository annotation was introduced as a marker for any class that fulfills the role or stereotype of a repository (a.k.a. Data Access Object or DAO). Among the possibilities for leveraging such a marker is the automatic translation of exceptions as described in Section 14.6.4, “Exception Translation”.

Spring 2.5 introduces further stereotype annotations: @Component, @Service and @Controller. @Component serves as a generic stereotype for any Spring-managed component; whereas, @Repository, @Service, and @Controller serve as specializations of @Component for more specific use cases (e.g., in the persistence, service, and presentation layers, respectively). What this means is that you can annotate your component classes with @Component, but by annotating them with @Repository, @Service, or @Controller instead, your classes are more properly suited for processing by tools or associating with aspects. For example, these stereotype annotations make ideal targets for pointcuts. Of course, it is also possible that @Repository, @Service, and @Controller may carry additional semantics in future releases of the Spring Framework. Thus, if you are making a decision between using @Component or @Service for your service layer, @Service is clearly the better choice. Similarly, as stated above, @Repository is already supported as a marker for automatic exception translation in your persistence layer.

4.12.2 Auto-detecting components

Spring provides the capability of automatically detecting 'stereotyped' classes and registering corresponding BeanDefinitions with the ApplicationContext. For example, the following two classes are eligible for such autodetection:

@Service
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}
@Repository
public class JpaMovieFinder implements MovieFinder {
    // implementation elided for clarity
}

To autodetect these classes and register the corresponding beans requires the inclusion of the following element in XML where 'basePackage' would be a common parent package for the two classes (or alternatively a comma-separated list could be specified that included the parent package of each class).

<?xml version="1.0" encoding="UTF-8"?>
<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-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd">
               
     <context:component-scan base-package="org.example"/>
     
</beans>
[Note]Note

Note that the scanning of classpath packages requires the presence of corresponding directory entries in the classpath. When building jars with Ant, make sure to not activate the files-only switch of the jar task!

Furthermore, the AutowiredAnnotationBeanPostProcessor and CommonAnnotationBeanPostProcessor are both included implicitly when using the component-scan element. That means that the two components are autodetected and wired together - all without any bean configuration metadata provided in XML.

[Note]Note

The registration of those post-processors can be disabled by including the annotation-config attribute with a value of 'false'.

4.12.3 Using filters to customize scanning

By default, classes annotated with @Component, @Repository, @Service, or @Controller (or classes annotated with a custom annotation that itself is annotated with @Component) are the only detected candidate components. However it is simple to modify and extend this behavior by applying custom filters. These can be added as either include-filter or exclude-filter sub-elements of the 'component-scan' element. Each filter element requires the 'type' and 'expression' attributes. Five filtering options exist as described below.

Table 4.7. Filter Types

Filter TypeExample ExpressionDescription
annotationorg.example.SomeAnnotationAn annotation to be present at the type level in target components.
assignableorg.example.SomeClassA class (or interface) that the target components are assignable to (extend/implement).
aspectjorg.example..*Service+An AspectJ type expression to be matched by the target components.
regexorg\.example\.Default.*A regex expression to be matched by the target components' class names.
customorg.example.MyCustomTypeFilterA custom implementation of the org.springframework.core.type.TypeFilter interface.

Find below an example of the XML configuration for ignoring all @Repository annotations and using "stub" repositories instead.

<beans ...>

     <context:component-scan base-package="org.example">
        <context:include-filter type="regex" expression=".*Stub.*Repository"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
     </context:component-scan>

</beans>
[Note]Note

It is also possible to disable the default filters by providing use-default-filters="false" as an attribute of the <component-scan/> element. This will in effect disable automatic detection of classes annotated with @Component, @Repository, @Service, or @Controller.

4.12.4 Using the @Configuration annotation

The central artifact in Spring's new Java-configuration support is the @Configuration-annotated class. These classes consist principally of @Bean-annotated methods that define instantiation, configuration, and initialization logic for objects that will be managed by the Spring IoC container.

Annotating a class with the @Configuration indicates that the class may be used by the Spring IoC container as a source of bean definitions. The simplest possible @Configuration class would read as follows:

@Configuration
public class AppConfig {

}            

An application may make use of one @Configuration-annotated class, or many. @Configuration is meta-annotated as a @Component, therefore Configuration-classes are candidates for component-scanning and may also take advantage of @Autowired annotations at the field and method level but not at the constructor level. Configuration-classes must also have a default constructor. Externalized values may be wired into Configuration-classes using the @Value annotation.

4.12.5 Using the @Bean annotation

@Bean is a method-level annotation and a direct analog of the XML <bean/> element. The annotation supports some of the attributes offered by <bean/>, such as: init-method, destroy-method, autowiring and name.

You can use the @Bean annotation in a Configuraton-class or in a Component-class.

4.12.5.1 Declaring a bean

To declare a bean, simply annotate a method with the @Bean annotation. Such a method will be used to register a bean definition within a BeanFactory of the type specified as the methods return value. By default, the bean name will be the same as the method name (see bean naming for details on how to customize this behavior). The following is a simple example of a @Bean method declaration:

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }

}                

For comparison sake, the configuration above is exactly equivalent to the following Spring XML:

<beans>
    <bean name="transferService" class="com.acme.TransferServiceImpl"/>
</beans>                

Both will result in a bean named transferService being available in the BeanFactory or ApplicationContext, bound to an object instance of type TransferServiceImpl:

transferService -> com.acme.TransferServiceImpl
                

4.12.5.2 Injecting dependencies

When @Beans have dependencies on one another, expressing that dependency is as simple as having one bean method call another:

@Configuration
public class AppConfig {

    @Bean
    public Foo foo() {
        return new Foo(bar());
    }

    @Bean
    public Bar bar() {
        return new Bar();
    }

}                

In the example above, the foo bean recevies a reference to bar via constructor injection.

4.12.5.3 Receiving lifecycle callbacks

Beans created in a Configuration-class supports the regular lifecycle callbacks. Any classes defined with the @Bean annotation can use the @PostConstruct and @PreDestroy annotations from JSR-250, see the section on JSR-250 annotations for further details.

The regular Spring lifecycle callbacks are fully supported as well. If a bean implements InitializingBean, DisposableBean, or Lifecycle, their respective methods will be called by the container.

The standard set of *Aware interfaces such as BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware, etc. are also fully supported.

The @Bean annotation supports specifying arbitrary initialization and destruction callback methods, much like Spring XML's init-method and destroy-method attributes to the bean element:

public class Foo {
    public void init() {
        // initialization logic
    }
}

public class Bar {
    public void cleanup() {
        // destruction logic
    }
}

@Configuration
public class AppConfig {
    @Bean(initMethodName = "init")
    public Foo foo() {
        return new Foo();
    }
    @Bean(destroyMethodName="cleanup")
    public Bar bar() {
        return new Bar();
    }
}

Of course, in the case of Foo above, it would be equally as valid to call the init() method directly during construction:

@Configuration
public class AppConfig {
    @Bean
    public Foo foo() {
        Foo foo = new Foo();
        foo.init();
        return foo;
    }

    // ...
}                    
[Tip]Tip

Remember that because you are working directly in Java, you can do anything you like with your objects, and do not always need to rely on the container!

4.12.5.4 Specifying bean scope

Using the @Scope annotation

You can specify that your beans defined with the @Bean annotation should have a specific scope. You can use any of the standard scopes specified in the Bean Scopes section.

The StandardScopes class provides string constants for each of these four scopes. SINGLETON is the default, and can be overridden by using the @Scope annotation:

@Configuration
public class MyConfiguration {
    @Bean
    @Scope(StandardScopes.PROTOTYPE)
    public Encryptor encryptor() {
        // ...
    }
}                 
@Scope and scoped-proxy

Spring offers a convenient way of working with scoped dependencies through scoped proxies. The easiest way to create such a proxy when using the XML configuration is the <aop:scoped-proxy/> element. Configuring your beans in Java with a @Scope annotation offers equivalent support with the proxyMode attribute. The default is no proxy (ScopedProxyMode.NO) but you can specify ScopedProxyMode.TARGET_CLASS or ScopedProxyMode.INTERFACES.

If we were to port the the XML reference documentation scoped proxy example (see link above) to our @Bean using Java, it would look like the following:

// a HTTP Session-scoped bean exposed as a proxy
@Bean
@Scope(value = StandardScopes.SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserPreferences userPreferences() {
   return new UserPreferences();
}

@Bean
public Service userService() {
   UserService service = new SimpleUserService();
   // a reference to the proxied 'userPreferences' bean
   service.seUserPreferences(userPreferences());
   return service;
}                
Lookup method injection

As noted earlier, lookup method injection is an advanced feature that should be comparatively rarely used. It is useful in cases where a singleton-scoped bean has a dependency on a prototype-scoped bean. Using Java for this type of configuration provides a natural means for implementing this pattern.

public abstract class CommandManager { 
    public Object process(Object commandState) { 
        // grab a new instance of the appropriate Command interface 
        Command command = createCommand(); 

        // set the state on the (hopefully brand new) Command instance 
        command.setState(commandState); 
        return command.execute(); 
    } 

    // okay... but where is the implementation of this method? 
    protected abstract Command createCommand(); 
}                   

Using Java-configurtion support we can easily create a subclass of CommandManager where the abstract createCommand() is overridden in such a way that it 'looks up' a brand new (prototype) command object:

@Bean
@Scope(StandardScopes.PROTOTYPE)
public AsyncCommand asyncCommand() {
    AsyncCommand command = new AsyncCommand();
    // inject dependencies here as required
    return command;
}

@Bean
public CommandManager commandManager() {
    // return new anonymous implementation of CommandManager with command() overridden
    // to return a new prototype Command object
    return new CommandManager() {
        protected Command command() {
            return asyncCommand();
        }
    }
}                    

4.12.5.5 Customizing bean naming

By default, Configuration-classes uses a @Bean method's name as the name of the resulting bean. This functionality can be overridden, however, using the name attribute.

@Configuration
public class AppConfig {

    @Bean(name = "bar")
    public Foo foo() {
        return new Foo();
    }

}        

4.12.6 Defining bean metadata within components

Spring components can also contribute bean definition metadata to the container. This is done with the same @Bean annotation used to define bean metadata within @Configuration annotated classes. Here is a simple example

@Component
public class FactoryMethodComponent {

  @Bean @Qualifier("public")
  public TestBean publicInstance() {
    return new TestBean("publicInstance");
  }

  public void DoWork() 
  {
    // Component method implementation omitted
  }
}

This class is a Spring component and has application specific code contained in its DoWork method. However, it also contributes a bean definition that has a factory method referring to the method publicInstance. The @Bean annotation identifies the factory method and also other bean definition properties, such as a qualifier value via the @Qualifier annotation. Other method level annotations that can be specified are @Scope, @Lazy, and custom qualifier annotations. Autowired fields and methods are supported as before with the additional support for autowiring of @Bean methods, as shown in the example below

@Component
public class FactoryMethodComponent {

 private static int i;

 @Bean @Qualifier("public")
 public TestBean publicInstance() { 
  return new TestBean("publicInstance");
 }

 // use of a custom qualifier and autowiring of method parameters

 @Bean @BeanAge(1)
 protected TestBean protectedInstance(@Qualifier("public") TestBean spouse, @Value("#{privateInstance.age}") String country) {
  TestBean tb = new TestBean("protectedInstance", 1);
  tb.setSpouse(tb);
  tb.setCountry(country);
  return tb;
 }

 @Bean @Scope(StandardScopes.PROTOTYPE)
 private TestBean privateInstance() {
  return new TestBean("privateInstance", i++);
 }

 @Bean @Scope(value = StandardScopes.SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
 public TestBean requestScopedInstance() {
  return new TestBean("requestScopedInstance", 3);
 }
}

Note the use of autowiring of the String method parameter country to the value of the Age property on another bean named privateInstance. A Spring Expression Language element is used to define the value of the property via the notation #{ <expression> }. For @Value annotations, an expression resolver is preconfigured to look for bean names when resolving expression text.

The @Bean methods in a Spring component are processed differently than their counterparts inside a Spring @Configuration class. The difference is that @Component classes are not enhanced with CGLIB to intercept the invocation of methods and fields. CGLIB proxying is the means by which invoking methods or fields within @Configuration classes' @Bean methods create bean metadata references to collaborating objects and do not invoke the method with normal Java semantics. In contrast, calling a method or field within a @Component classes' @Bean method has standard Java semantics.

4.12.7 Naming autodetected components

When a component is autodetected as part of the scanning process, its bean name will be generated by the BeanNameGenerator strategy known to that scanner. By default, any Spring 'stereotype' annotation (@Component, @Repository, @Service, and @Controller) that contains a name value will thereby provide that name to the corresponding bean definition. If such an annotation contains no name value or for any other detected component (such as those discovered due to custom filters), the default bean name generator will return the uncapitalized non-qualified class name. For example, if the following two components were detected, the names would be 'myMovieLister' and 'movieFinderImpl':

@Service("myMovieLister")
public class SimpleMovieLister {
    // ...
}
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}
[Note]Note

If you don't want to rely on the default bean-naming strategy, you may provide a custom bean-naming strategy. First, implement the BeanNameGenerator interface, and be sure to include a default no-arg constructor. Then, provide the fully-qualified class name when configuring the scanner:

<beans ...>
               
     <context:component-scan base-package="org.example"
                             name-generator="org.example.MyNameGenerator" />

</beans>

As a general rule, consider specifying the name with the annotation whenever other components may be making explicit references to it. On the other hand, the auto-generated names are adequate whenever the container is responsible for wiring.

4.12.8 Providing a scope for autodetected components

As with Spring-managed components in general, the default and by far most common scope is 'singleton'. However, there are times when other scopes are needed. Therefore Spring 2.5 introduces a new @Scope annotation as well. Simply provide the name of the scope within the annotation, such as:

@Scope(StandardScopes.PROTOTYPE)
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}
[Note]Note

If you would like to provide a custom strategy for scope resolution rather than relying on the annotation-based approach, implement the ScopeMetadataResolver interface, and be sure to include a default no-arg constructor. Then, provide the fully-qualified class name when configuring the scanner:

<beans ...>
               
     <context:component-scan base-package="org.example"
                             scope-resolver="org.example.MyScopeResolver" />
     
</beans>

When using certain non-singleton scopes, it may be necessary to generate proxies for the scoped objects. The reasoning is described in detail within the section entitled Section 4.4.4.5, “Scoped beans as dependencies”. For this purpose, a scoped-proxy attribute is available on the 'component-scan' element. The three possible values are: 'no', 'interfaces', and 'targetClass'. For example, the following configuration will result in standard JDK dynamic proxies:

<beans ...>
               
     <context:component-scan base-package="org.example"
                             scoped-proxy="interfaces" />
     
</beans>

4.12.9 Providing qualifier metadata with annotations

The @Qualifier annotation was introduced in the section above entitled Section 4.11.3, “Fine-tuning annotation-based autowiring with qualifiers”. The examples in that section demonstrated use of the @Qualifier annotation as well as custom qualifier annotations to provide fine-grained control when resolving autowire candidates. Since those examples were based on XML bean definitions, the qualifier metadata was provided on the candidate bean definitions using the 'qualifier' or 'meta' sub-elements of the 'bean' element in the XML. When relying upon classpath scanning for autodetection of components, then the qualifier metadata may be provided with type-level annotations on the candidate class. The following three examples demonstrate this technique.

@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}
@Component
@Genre("Action")
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}
@Component
@Offline
public class CachingMovieCatalog implements MovieCatalog {
    // ...
}
[Note]Note

As with most of the annotation-based alternatives, keep in mind that the annotation metadata is bound to the class definition itself, while the use of XML allows for multiple beans of the same type to provide variations in their qualifier metadata since that metadata is provided per-instance rather than per-class.