|
AOP
Since 2003 there has been much interest in applying AOP solutions to those
enterprise concerns, such as transaction management, which have traditionally
been addressed by EJB.
The first goal of Spring's AOP support is to provide J2EE services to POJOs.
Spring AOP is portable between application servers, so there's no risk of
vendor lock in. It works in either web or EJB container, and has been used
successfully in WebLogic, Tomcat, JBoss, Resin, Jetty, Orion and many other
application servers and web containers.
Spring AOP supports method interception. Key AOP concepts supported include:
-
Interception: Custom behaviour can be inserted before or after method
invocations against any interface or class. This is similar to "around
advice" in AspectJ terminology.
-
Introduction: Specifying that an advice should cause an object to
implement additional interfaces. This can amount to mixin inheritance.
-
Static and dynamic pointcuts: Specifying the points in program
execution at which interception should take place. Static pointcuts concern
method signatures; dynamic pointcuts may also consider method arguments at
the point where they are evaluated. Pointcuts are defined separately from
interceptors, enabling a standard interceptor to be applied in different
applications and code contexts.
Spring supports both stateful (one instance per advised object) and stateless
interceptors (one instance for all advice).
Spring does not support field interception. This is a deliberate design
decision. I have always felt that field interception violates encapsulation. I
prefer to think of AOP as complementing, rather than conflicting with,
OOP. In five or ten years time we will probably have travelled a lot farther
on the AOP learning curve and feel comfortable giving AOP a seat at the top
table of application design. (At that point language-based solutions such as
AspectJ may be far more attractive than they are today.)
Spring implements AOP using dynamic proxies (where an interface exists) or
CGLIB byte code generation at runtime (which enables proxying of classes).
Both these approaches work in any application server, or in a standalone
environment.
Spring was the first AOP framework to implement the AOP Alliance interfaces
(www.sourceforge.net/projects/aopalliance).
These represent an attempt to define interfaces allowing interoperability of
interceptors between AOP frameworks.
Spring integrates with AspectJ, providing the ability to seamlessly include
AspectJ aspects into Spring applications . Since Spring 1.1 it has been
possible to dependency inject AspectJ aspects using the Spring IoC container,
just like any Java class. Thus AspectJ aspects can depend on any
Spring-managed objects. The integration with the forthcoming AspectJ 5 release
is still more exciting, with AspectJ set to provide the ability to dependency
inject any POJO using Spring, based on an annotation-driven pointcut.
Because Spring advises objects at instance, rather than class loader, level,
it is possible to use multiple instances of the same class with different
advice, or use unadvised instances along with advised instances.
Perhaps the commonest use of Spring AOP is for declarative transaction
management. This builds on the transaction abstraction described above, and
can deliver declarative transaction management on any POJO. Depending on the
transaction strategy, the underlying mechanism can be JTA, JDBC, Hibernate or
any other API offering transaction management.
The following are the key differences from EJB CMT:
-
Transaction management can be applied to any POJO. We recommend that
business objects implement interfaces, but this is a matter of good
programming practice, and is not enforced by the framework.
-
Programmatic rollback can be achieved within a transactional POJO through
using the Spring transaction API. We provide static methods for this, using
ThreadLocal variables, so you don't need to propagate a context object such
as an EJBContext to ensure rollback.
-
You can define rollback rules declaratively. Whereas EJB will not
automatically roll back a transaction on an uncaught application exception
(only on unchecked exceptions, other types of Throwable and "system"
exceptions), application developers often want a transaction to roll back on
any exception. Spring transaction management allows you to specify
declaratively which exceptions and subclasses should cause automatic
rollback. Default behaviour is as with EJB, but you can specify automatic
rollback on checked, as well as unchecked exceptions. This has the important
benefit of minimizing the need for programmatic rollback, which creates a
dependence on the Spring transaction API (as EJB programmatic rollback does
on the EJBContext).
-
Because the underlying Spring transaction abstraction supports savepoints if
they are supported by the underlying transaction infrastructure, Spring's
declarative transaction management can support nested transactions,
in addition to the propagation modes specified by EJB CMT (which Spring
supports with identical semantics to EJB). Thus, for example, if you have
doing JDBC operations on Oracle, you can use declarative nested transactions
using Spring.
-
Transaction management is not tied to JTA. As explained above, Spring
transaction management can work with different transaction strategies.
It's also possible to use Spring AOP to implement application-specific
aspects. Whether or not you choose to do this depends on your level of comfort
with AOP concepts, rather than Spring's capabilities, but it can be very
useful. Successful examples we've seen include:
-
Custom security interception, where the complexity of security checks
required is beyond the capability of the standard J2EE security
infrastructure. (Of course, before rolling your own security infrastructure,
you should check the capabilities of
Acegi Security for Spring,
a powerful, flexible security framework that integrates with Spring using
AOP, and reflects Spring's architectural approach.
-
Debugging and profiling aspects for use during development
-
Aspects that apply consistent exception handling policies in a single place
-
Interceptors that send emails to alert administrators or users of unusual
scenarios
Application-specific aspects can be a powerful way of removing the need for
boilerplate code across many methods.
Spring AOP integrates transparently with the Spring BeanFactory concept. Code
obtaining an object from a Spring BeanFactory doesn't need to know whether or
not it is advised. As with any object, the contract will be defined by the
interfaces the object implements.
The following XML stanza illustrates how to define an AOP proxy:
<bean id="myTest" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>org.springframework.beans.ITestBean</value> </property> <property name="interceptorNames"> <list> <value>txInterceptor</value> <value>target</value> </list> </property> </bean>
Note that the class of the bean definition is always the AOP framework's
ProxyFactoryBean, although the type of the bean as used in references or
returned by the BeanFactory getBean() method will depend on the proxy
interfaces. (Multiple proxy methods are supported.) The "interceptorNames"
property of the ProxyFactoryBean takes a list of String. (Bean names must be
used rather than bean references, as new instances of stateful interceptors
may need to be created if the proxy is a "prototype", rather than a singleton
bean definition.) The names in this list can be interceptors or pointcuts
(interceptors and information about when they should apply). The "target"
value in the list above automatically creates an "invoker interceptor"
wrapping the target object. It is the name of a bean in the factory that
implements the proxy interface. The myTest bean in this example can be used
like any other bean in the bean factory. For example, other objects can
reference it via <ref> elements and these references will be set by
Spring IoC.
There are a number of ways to set up proxying more concisely, if you don't
need the full power of the AOP framework, such as using Java 5.0 annotations
to drive transactional proxying without XML metadata, or the ability to use a
single piece of XML to apply a consistent proxying strategy to many beans
defined in a Spring factory.
It's also possible to construct AOP proxies programmatically without using a
BeanFactory, although this is more rarely used:
TestBean target = new TestBean(); DebugInterceptor di = new DebugInterceptor(); MyInterceptor mi = new MyInterceptor(); ProxyFactory factory = new ProxyFactory(target); factory.addInterceptor(0, di); factory.addInterceptor(1, mi); // An "invoker interceptor" is automatically added to wrap the target ITestBean tb = (ITestBean) factory.getProxy();
We believe that it's generally best to externalize the wiring of applications
from Java code, and AOP is no exception.
The use of AOP as an alternative to EJB (version 2 or above) for delivering
enterprise services is growing in importance. Spring has successfully
demonstrated the value proposition.
|