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 |
---|---|
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
|
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.
Spring provides the capability of automatically detecting
'stereotyped' classes and registering corresponding
BeanDefinition
s 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 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 |
---|---|
The registration of those post-processors can be disabled by including the annotation-config attribute with a value of 'false'. |
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 Type | Example Expression | Description |
---|---|---|
annotation | org.example.SomeAnnotation | An annotation to be present at the type level in target components. |
assignable | org.example.SomeClass | A class (or interface) that the target components are assignable to (extend/implement). |
aspectj | org.example..*Service+ | An AspectJ type expression to be matched by the target components. |
regex | org\.example\.Default.* | A regex expression to be matched by the target components' class names. |
custom | org.example.MyCustomTypeFilter | A 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 |
---|---|
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
|
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.
@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.
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
When @Bean
s 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.
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 |
---|---|
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! |
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() { // ... } }
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; }
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(); } } }
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(); } }
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.
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 |
---|---|
If you don't want to rely on the default bean-naming strategy,
you may provide a custom bean-naming strategy. First, implement the
|
<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.
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 |
---|---|
If you would like to provide a custom strategy for scope
resolution rather than relying on the annotation-based approach,
implement the |
<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>
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 |
---|---|
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. |