29-03-2012, 01:35 PM
Annotations and Source Level Metadata Support
25.1. Introduction
Source-level metadata is the addition of attributes or annotations to program elements - usually, classes and/or
methods.
Both of these examples use Jakarta Commons Attributes syntax.
Source-level metadata was introduced to the mainstream by XDoclet (in the Java world) and by the release of
Microsoft's .NET platform, which uses source-level attributes to control transactions, pooling and other
behavior.
The value in this approach has been recognized in the J2EE community. For example, it's much less verbose
than the traditional XML deployment descriptors used exclusively by EJB. While it is desirable to externalize
some things from program source code, some important enterprise settings - notably transaction characteristics
- arguably belong in program source. Contrary to the assumptions of the EJB spec, it seldom makes sense to
modify the transactional characteristics of a method (although parameters like transaction timeouts might
change!).
Although metadata attributes are typically used mainly by framework infrastructure to describe the services
application classes require, it should also be possible for metadata attributes to be queried at runtime. This is a
key distinction from solutions such as XDoclet, which view metadata primarily as a way of generating code
such as EJB artefacts.
There are a number of solutions in this space, including:
• Standard Java Annotations: the standard Java metadata implementation (developed as JSR-175 and
available in Java 5). Spring has specific Java 5 annotations for transactional demarcation, JMX, and aspects
(to be precise they are AspectJ annotations). However, since Spring supports Java 1.4 as well, a solution for
said JVM versions is needed too. Spring metadata support provides such a solution.
• XDoclet: well-established solution, primarily intended for code generation.
Spring Framework (2.5.6) 520
• Various open source attribute implementations, for Java 1.4, of which Commons Attributes is the most
complete implementation. All these require a special pre- or post-compilation step.
. Spring's metadata support
In keeping with its provision of abstractions over important concepts, Spring provides a facade to metadata
implementations, in the form of the org.springframework.metadata.Attributes interface. Such a facade
adds value for several reasons:
• Even though Java 5 provides metadata support at language level, there will still be value in providing such an
abstraction:
• Java 5 metadata is static. It is associated with a class at compile time, and cannot be changed in a deployed
environment (annotation state can actually be changed at runtime using reflection, but doing so would
really be a bad practice). There is a need for hierarchical metadata, providing the ability to override certain
attribute values in deployment - for example, in an XML file.
• Java 5 metadata is returned through the Java reflection API. This makes it impossible to mock during test
time. Spring provides a simple interface to allow this.
• There will be a need for metadata support in 1.3 and 1.4 applications for at least two years. Spring aims to
provide working solutions now; forcing the use of Java 5 is not an option in such an important area.
• Current metadata APIs, such as Commons Attributes (used by Spring 1.0-1.2) are hard to test. Spring
provides a simple metadata interface that is much easier to mock.
The Spring Attributes interface looks like this:
public interface Attributes {
Collection getAttributes(Class targetClass);
Collection getAttributes(Class targetClass, Class filter);
Collection getAttributes(Method targetMethod);
Collection getAttributes(Method targetMethod, Class filter);
Collection getAttributes(Field targetField);
Collection getAttributes(Field targetField, Class filter);
}
This is a lowest common denominator interface. JSR-175 offers more capabilities than this, such as attributes
on method arguments.
Note that this interface offers Object attributes, like .NET. This distinguishes it from attribute systems such as
that of Nanning Aspects, which offer only String attributes. There is a significant advantage in supporting
Object attributes, namely that it enables attributes to participate in class hierarchies and allows such attributes
to react intelligently to their configuration parameters.
With most attribute providers, attribute classes are configured via constructor arguments or JavaBean
properties. Commons Attributes supports both.
As with all Spring abstraction APIs, Attributes is an interface. This makes it easy to mock attribute
implementations for unit tests.
Annotations and Source Level Metadata Support
Spring Framework (2.5.6) 521
25.3. Annotations
The Spring Framework ships with a number of custom Java 5+ annotations.
25.3.1. @Required
The @Required annotation in the org.springframework.beans.factory.annotation package can be used to
mark a property as being 'required-to-be-set' (i.e. an annotated (setter) method of a class must be configured to
be dependency injected with a value), else an Exception will be thrown by the container at runtime.
The best way to illustrate the usage of this annotation is to show an
At runtime the following message will be generated by the Spring container (the rest of the stack trace has been
truncated).
Exception in thread "main" java.lang.IllegalArgumentException:
Property 'movieFinder' is required for bean 'movieLister'.
There is one last little (small, tiny) piece of Spring configuration that is required to actually 'switch on' this
behavior. Simply annotating the 'setter' properties of your classes is not enough to get this behavior. You need
to enable a component that is aware of the @Required annotation and that can process it appropriately.
This component is the RequiredAnnotationBeanPostProcessor class. This is a special BeanPostProcessor
implementation that is @Required-aware and actually provides the 'blow up if this required property has not
been set' logic. It is very easy to configure; simply drop the following bean definition into your Spring XML
configuration.
Here is the source code for the @Mandatory annotation. You will need to ensure that your custom annotation
type is itself annotated with appropriate annotations for its target and runtime retention policy.
package your.company.package;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Mandatory {
}
25.3.2. Other @Annotations in Spring
Annotations are also used in a number of other places throughout Spring. Rather than being described here,
these annotations are described in that section or chapter of the reference documentation to which they are most
relevant.
• Section 9.5.6, “Using @Transactional”
• Section 6.8.1, “Using AspectJ to dependency inject domain objects with Spring”
• Section 6.2, “@AspectJ support”
• Section 3.11, “Annotation-based configuration”
• Section 3.12, “Classpath scanning for managed components”
25.4. Integration with Jakarta Commons Attributes
Presently Spring supports only Jakarta Commons Attributes out of the box, although it is easy to provide
implementations of the org.springframework.metadata.Attributes interface for other metadata providers.
Commons Attributes 2.2 (http://jakarta.apachecommons/attributes/) is a capable attributes solution. It
supports attribute configuration via constructor arguments and JavaBean properties, which offers better
self-documentation in attribute definitions. (Support for JavaBean properties was added at the request of the
Spring team.)
We've already seen two examples of Commons Attributes attributes definitions. In general, we will need to
express:
• The name of the attribute class. This can be a fully qualified name (FQN), as shown above. If the relevant
attribute class has already been imported, the FQN isn't required. It's also possible to specify "attribute
packages" in attribute compiler configuration.
Annotations and Source Level Metadata Support
Spring Framework (2.5.6) 523
• Any necessary parameterization. This is done via constructor arguments or JavaBean properties.
Bean properties look as follows:
It's possible to combine constructor arguments and JavaBean properties (as in Spring IoC).
Because, unlike Java 1.5 attributes, Commons Attributes is not integrated with the Java language, it is
necessary to run a special attribute compilation step as part of the build process.
To run Commons Attributes as part of the build process, you will need to do the following:
1. Copy the necessary library jars to $ANT_HOME/lib. Four Jars are required, and all are distributed with Spring:
• the Commons Attributes compiler jar and API jar
• xJavadoc.jar from XDoclet
• commons-collections.jar from Jakarta Commons
2. Import the Commons Attributes ant tasks into your project build script, as follows:
<taskdef resource="org/apache/commons/attributes/anttasks.properties"/>
3. Next, define an attribute compilation task, which will use the Commons Attributes attribute-compiler task to
"compile" the attributes in the source. This process results in the generation of additional sources, to a location
specified by the destdir attribute. Here we show the use of a temporary directory for storing the generated
files:
The compile target that runs javac over the sources should depend on this attribute compilation task, and must
also compile the generated sources, which we output to our destination temporary directory. If there are syntax
errors in your attribute definitions, they will normally be caught by the attribute compiler. However, if the
attribute definitions are syntactically plausible, but specify invalid types or class names, the compilation of the
generated attribute classes may fail. In this case, you can look at the generated classes to establish the cause of
the problem.
Commons Attributes also provides Maven support. Please refer to Commons Attributes documentation for
further information.
While this attribute compilation process may look complex, in fact it's a one-off cost. Once set up, attribute
compilation is incremental, so it doesn't usually noticeably slow the build process. And once the compilation
process is set up, you may find that use of attributes as described in this chapter can save you a lot of time in
other areas.
If you require attribute indexing support (only currently required by Spring for attribute-targeted web
Annotations and Source Level Metadata Support
Spring Framework (2.5.6) 524
controllers, discussed below), you will need an additional step, which must be performed on a jar file of your
compiled classes. In this additional step, Commons Attributes will create an index of all the attributes defined
on your sources, for efficient lookup at runtime. The step looks like this:
See the /attributes directory of the Spring JPetStore sample application for an example of this build process.
You can take the build script it contains and modify it for your own projects.
If your unit tests depend on attributes, try to express the dependency on the Spring Attributes abstraction, rather
than Commons Attributes. Not only is this more portable - for example, your tests will still work if you switch
to Java 1.5 attributes in future - it simplifies testing. Also, Commons Attributes is a static API, while Spring
provides a metadata interface that you can easily mock.
25.5. Metadata and Spring AOP autoproxying
The most important uses of metadata attributes are in conjunction with Spring AOP. This provides a .NET-like
programming model, where declarative services are automatically provided to application objects that declare
metadata attributes. Such metadata attributes can be supported out of the box by the framework, as in the case
of declarative transaction management, or can be custom.
25.5.1. Fundamentals
This builds on the Spring AOP autoproxy functionality. Configuration might look like this:
The basic concepts here should be familiar from the discussion of autoproxying in the AOP chapter.
The most important bean definitions are the auto-proxy creator and the advisor. Note that the actual bean names
are not important; what matters is their class.
The bean definition of class
org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator will automatically
advise ("auto-proxy") all bean instances in the current factory based on matching advisor implementations. This
class knows nothing about attributes, but relies on advisors' pointcuts matching. The pointcuts, however, do
know about attributes.
Thus we simply need an AOP advisor that will provide declarative transaction management based on attributes.
Annotations and Source Level Metadata Support
Spring Framework (2.5.6) 525
It is possible to add arbitrary custom advisor implementations as well, and they will also be evaluated and
applied automatically. (You can use advisors whose pointcuts match on criteria besides attributes in the same
autoproxy configuration, if necessary.)
Finally, the attributes bean is the Commons Attributes Attributes implementation. Replace it with another
implementation of the org.springframework.metadata.Attributes interface to source attributes from a
different source.
25.5.2. Declarative transaction management
The most common use of source-level attributes is to provide declarative transaction management. Once the
bean definitions shown above are in place, you can define any number of application objects requiring
declarative transactions. Only those classes or methods with transaction attributes will be given transaction
advice. You need to do nothing except define the required transaction attributes.
Please note that you can specify transaction attributes at either class or method level. Class-level attributes, if
specified, will be "inherited" by all methods whereas method attributes will wholly override any class-level
attributes.