The Spring container instantiates and configures beans defined in the context of your application. You can also ask
the bean factory to configure an already existing object by specifying the name of the bean definition that contains
the configuration that you want to apply. spring-aspects.jar
contains an annotation-driven aspect that
uses this capability to perform dependency injection on any object. Support is intended for objects created outside
the control of any container. Domain objects often fall into this category because they are often created
programmatically using the new
operator or using an object-relational mapping (ORM) tool as a result of
a database query.
The @Configurable
annotation marks the class as suitable for Spring-based
configuration. In the simplest case, you can use it as a purely marker annotation, as shown in the following
example:
package com.xyz.myapp.domain;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable
public class Account {
// ...
}
package com.xyz.myapp.domain
import org.springframework.beans.factory.annotation.Configurable
@Configurable
class Account {
// ...
}
When used this way as a token interface Spring configures new instances of an annotated type (in this case
Account
) with a bean definition (usually scoped at the prototype level) with the same name as the fully
qualified type name(com.xyz.myapp.domain.Account
). Because the default name for a bean is the fully
qualified name of its type, a convenient way to declare a prototype definition is to omit the id
attribute, as shown in the following example:
<bean class="com.xyz.myapp.domain.Account" scope="prototype">
<property name="fundsTransferService" ref="fundsTransferService"/>
</bean>
If you need to explicitly specify the name of a prototype bean definition for later use, you can do so directly in the annotation, as shown in the following example:
package com.xyz.myapp.domain;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable("account")
public class Account {
// ...
}
package com.xyz.myapp.domain
import org.springframework.beans.factory.annotation.Configurable
@Configurable("account")
class Account {
// ...
}
Spring will now look for a bean definition named account
and use that as the definition for
configuring new instances of Account
.
You can also use automatic detection and linking to avoid
specifying a dedicated bean definition at all. To have Spring apply automatic discovery and wiring, use the autowire
property of the @Configurable
annotation. You can specify either @Configurable(autowire=Autowire.BY_TYPE)
or @Configurable(autowire=Autowire.BY_NAME)
to automatically detect and associate by type or by name,
respectively. As an alternative, it is preferable to specify explicit, annotation-based dependency injection for
your @Configurable
annotated beans via the @Autowired
or @Inject
annotations
at the field level or method.
Finally, you can enable Spring dependency checking for object references in a
newly created and configured object using the dependencyCheck
attribute (for example, @Configurable(autowire=Autowire.BY_NAME,dependencyCheck=true)
).
If this attribute is set to true
, then after configuration
is complete, Spring will check that all properties (that are not primitives or collections) have been set.
Note that using the annotation itself is gives nothing to himself. It is the
AnnotationBeanConfigurerAspect
in spring-aspects.jar
that acts when an annotation is
present. Essentially, the aspect says: "Upon returning from initializing a new object of type annotated with @Configurable
,
configure the newly created object using Spring according to the properties of the annotation." In this context,
"initialization" refers to newly created instances of objects (for example, objects instantiated using the
new
operator), as well as Serializable
objects that are deserialized (for example, via readResolve()).
One of the key phrases in the above paragraph is "as a matter of fact." In
most cases, the exact semantics of "on return from initializing a new object" will be appropriate. In this context,
"after initialization" means that dependencies are injected after the object is constructed. This means that
dependencies are not available for use in class constructor bodies. If you need to inject dependencies before the
constructor bodies begin executing, and thus make them available for use in the constructor body, you need to define
this in the @Configurable
annotation declaration as follows:
@Configurable(preConstruction = true)
@Configurable(preConstruction = true)
For more information on the language semantics of the various slice types in AspectJ, you can find in this appendix to AspectJ Programming Guide.
For this to work, the annotated types must be bound using AspectJ binding tool.
To do this, you can use either an Ant or Maven task at build time (see for example AspectJ
Development Environment Guide), or linking at load time. The AnnotationBeanConfigurerAspect
itself must be configured by Spring (to obtain a reference to the bean factory that will be used to configure
new
objects). If you are using Java based configuration, then you can add @EnableSpringConfigured
to
any
@Configuration
class as shown below:
@Configuration
@EnableSpringConfigured
public class AppConfig {
}
@Configuration
@EnableSpringConfigured
class AppConfig {
}
If you prefer XML-based configuration, the context
namespace in Spring defines a convenience
element context:spring-configured
, which can be used like this:
<context:spring-configured/>
If object instances marked with the @Configurable
annotation are created before the aspect has
been configured, which results in a message appearing in the debug log and the object configuration fails. An
example would be a bean in a Spring configuration that creates domain objects when it is initialized by Spring.
In
this case, you can use the bean's depends-on
attribute to manually indicate that the bean depends
on a
configuration aspect. The following example shows how to use the depends-on
attribute:
<bean id="myService"
class="com.xzy.myapp.service.MyService"
depends-on="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect">
<!-- ... -->
</bean>
@Configurable
handling via aspect of the
bean's
configurator, unless you need to use its semantics at runtime. In particular, make sure that you do not use the
@Configurable
annotation on bean classes that are registered in the container as regular Spring
beans.
This will result in double initialization - once through the container and once through the aspect.
Unit testing objects annotated with @Configurable
One of the purposes of supporting the
@Configurable
annotation is the ability to independently unit test domain objects without the
hassle of hard-coded sequential lookups. If types marked with the @Configurable
annotation have not
been bound by AspectJ, then the annotation will have no effect on unit testing. You can set property references
to a
mock object or stub function in the object under test and continue working as normal. If types with the @Configurable
annotation have been bound by AspectJ, you will still be able to unit test outside the container as normal, but
whenever you construct an object with the @Configurable
annotation you will see warning message
indicating that it has not been configured by Spring.
Working with multiple application contexts
AnnotationBeanConfigurerAspect
, which is used to implement support @Configurable
, is a
single instance of an AspectJ aspect. The scope of a single instance of an aspect is the same as the scope of
static
members: For each class loader, there is one type-defining aspect instance. This means that if you are defining
multiple application contexts within the same class loader hierarchy, you need to think about where to define
the
bean annotated with @EnableSpringConfigured
and where to place spring-aspects.jar
on
the
classpath.
Consider a typical Spring web application configuration, which has a common parent application
context defining common business services, everything needed to support those services, and one child
application
context for each servlet ( which contains definitions specific to this servlet). All of these contexts coexist
in
the same class loader hierarchy, and therefore AnnotationBeanConfigurerAspect
can only contain a
reference to one of them. In this case, it is recommended to define the @EnableSpringConfigured
bean in
the general (parent) application context. This identifies the services that you might want to inject into your
domain objects. The consequence of this is that you will not be able to configure domain objects with references
to
beans defined in child (servlet-specific) contexts using the @Configurable annotation mechanism (which you
probably
wouldn't want anyway).
When deploying multiple web applications in the same container, ensure that each web
application loads types from spring-aspects.jar
using its own class loader (for example, by placing
spring-aspects.jar
in WEB-INF/lib
). If spring-aspects.jar
is only added
to
the general container-wide classpath (and therefore loaded a common parent class loader), all web applications
will
use the same aspect instance (which you probably don't want).