- 论坛徽章:
- 0
|
6.1. Introduction
Aspect-Oriented Programming
(AOP) complements Object-Oriented Programming (OOP)
by providing another way of thinking about program structure. In addition
to classes, AOP gives you aspects. Aspects
enable modularization of concerns such as transaction management that
cut across multiple types and objects. (Such concerns are often
termed crosscutting concerns.)
One of the key components of Spring is the AOP
framework. While the Spring IoC container does not depend on AOP,
meaning you don't need to use AOP if you don't want to, AOP complements
Spring IoC to provide a very capable middleware solution.
Spring 2.0 AOP
Spring 2.0 introduces a simpler and more powerful
way of writing custom aspects using either a schema-based approach
or the @AspectJ annotation style. Both of these styles offer fully
typed advice and use of the AspectJ pointcut language, while still using
Spring AOP for weaving.
The Spring 2.0 schema and @AspectJ based AOP support is discussed in
this chapter. Spring 2.0 AOP remains fully backwards compatible with
Spring 1.2 AOP, and the lower-level AOP support offered by the Spring 1.2 APIs
is discussed in
the following chapter
.
AOP is used in the Spring Framework:
To provide declarative enterprise services, especially as a
replacement for EJB declarative services. The most important such
service is
declarative transaction management
,
which builds on the Spring Framework's transaction abstraction.
To allow users to implement custom aspects, complementing their
use of OOP with AOP.
If you are interested only in generic declarative services or
other pre-packaged declarative middleware services such as pooling, you
don't need to work directly with Spring AOP, and can skip most of this
chapter.6.1.1. AOP concepts
Let us begin by defining some central AOP concepts. These terms
are not Spring-specific. Unfortunately, AOP terminology is not
particularly intuitive; however, it would be even more confusing if
Spring used its own terminology.
Aspect: A modularization of a concern
that cuts across multiple objects. Transaction management is a good example
of a crosscutting concern in J2EE applications. In Spring AOP, aspects are
implemented using regular classes (the schema-based approach) or regular
classes annotated with the @Aspect annotation
(@AspectJ style).
Join point: A point during the execution of
a program, such as the execution of a method or the handling of
an exception. In Spring AOP, a join point always represents
a method execution. Join point information is available in advice
bodies by declaring a parameter of type org.aspectj.lang.JoinPoint.
Advice: Action taken by an aspect
at a particular join point. Different types of advice include
"around," "before" and "after" advice. Advice types are discussed
below. Many AOP frameworks, including Spring, model an advice as an
interceptor, maintaining a chain of
interceptors "around" the join point.
Pointcut: A predicate that matches join points.
Advice is associated with a pointcut expression and runs at any join
point matched by the pointcut (for example, the execution of a method with
a certain name). The concept of join points as matched by pointcut
expressions is central to AOP: Spring uses the AspectJ pointcut language by default.
Introduction: (Also known as an
inter-type declaration). Declaring additional methods or fields on
behalf of a type. Spring AOP allows you to introduce new interfaces
(and a corresponding implementation) to
any proxied object. For example, you could use an introduction to
make a bean implement an IsModified
interface, to simplify caching.
Target object: Object being advised
by one or more aspects. Also referred to as the advised
object. Since Spring AOP is implemented using runtime proxies, this object
will always be a proxied object.
AOP proxy: An object created by the AOP
framework in order to implement the aspect contracts (advise method
executions and so on). In the Spring Framework, an AOP proxy will be a JDK
dynamic proxy or a CGLIB proxy. Proxy creation is
transparent to users of the schema-based and @AspectJ styles of
aspect declaration introduced in Spring 2.0.
Weaving: Linking aspects with other
application types or objects to create an advised object. This can be
done at compile time (using the AspectJ compiler, for example), load time,
or at runtime. Spring AOP, like other pure Java AOP frameworks, performs
weaving at runtime.
Types of advice:
Before advice: Advice that executes
before a join point, but which does not have the ability to prevent
execution flow proceeding to the join point (unless it throws an
exception).
After returning advice: Advice to be
executed after a join point completes normally: for example, if a
method returns without throwing an exception.
After throwing advice: Advice to be executed if a
method exits by throwing an exception.
After (finally) advice: Advice to be executed regardless
of the means by which a join point exits (normal or exceptional return).
Around advice: Advice that surrounds a
join point such as a method invocation. This is the most powerful
kind of advice. Around advice can perform custom behavior before
and after the method invocation. It is also responsible for choosing
whether to proceed to the join point or to shortcut the advised method
execution by returning its own return value or throwing an exception.
Around advice is the most general kind of advice. Since Spring AOP,
like AspectJ, provides a full range of advice types, we recommend that you
use the least powerful advice type that can implement the required behavior.
For example, if you need only to update a cache with the return value of a
method, you are better off implementing an after returning advice than an
around advice, although an around advice can accomplish the same thing. Using
the most specific advice type provides a simpler programming model with less
potential for errors. For example, you do not need to invoke the
proceed() method on the JoinPoint
used for around advice, and hence cannot fail to invoke it.
In Spring 2.0, all advice parameters are statically typed, so that you
work with advice parameters of the appropriate type (the type of the return
value from a method execution for example) rather than
Object arrays.
The concept of join points, matched by pointcuts, is the key to AOP
which distinguishes it from older technologies offering only interception.
Pointcuts enable advice to be targeted independently of the Object-Oriented hierarchy.
For example, an around advice providing declarative transaction management can
be applied to a set of methods spanning multiple objects (such as all business
operations in the service layer).
6.1.2. Spring AOP capabilities and goals
Spring AOP is implemented in pure Java. There is no need for a
special compilation process. Spring AOP does not need to control the
class loader hierarchy, and is thus suitable for use in a J2EE web
container or application server.
Spring AOP currently supports only method execution join points
(advising the execution of methods on Spring beans). Field interception
is not implemented, although support for field interception could be added
without breaking the core Spring AOP APIs. If you need to advise field
access and update join points, consider a language such as AspectJ.
Spring AOP's approach to AOP differs from that of most other AOP
frameworks. The aim is not to provide the most complete AOP
implementation (although Spring AOP is quite capable); it is rather to
provide a close integration between AOP implementation and Spring IoC to
help solve common problems in enterprise applications.
Thus, for example, the Spring Framework's AOP functionality is normally
used in conjunction with the Spring IoC container. Aspects are configured
using normal bean definition syntax (although this allows powerful
"autoproxying" capabilities): this is a crucial difference from other AOP
implementations. There are some things you cannot do easily or
efficiently with Spring AOP, such as advise very fine-grained objects:
AspectJ is the best choice in such cases. However, our experience is that
Spring AOP provides an excellent solution to most problems in J2EE applications
that are amenable to AOP.
Spring AOP will never strive to compete with AspectJ to provide
a comprehensive AOP solution. We believe that both proxy-based frameworks like
Spring AOP and full-blown frameworks such as AspectJ are valuable, and that they
are complementary, rather than in competition. Spring 2.0 seamlessly integrates
Spring AOP and IoC with AspectJ, to enable all uses of AOP to be catered for within
a consistent Spring-based application architecture. This integration does not
affect the Spring AOP API or the AOP Alliance API: Spring AOP remains
backward-compatible. See
the following chapter
for a
discussion of the Spring AOP APIs.
![]()
Note
One of the central tenets of the Spring Framework is that of
non-invasiveness; this is the idea that you should not be forced to
introduce framework-specific classes and interfaces into your business/domain
model. However, in some places the Spring Framework does give you the option
to introduce Spring Framework-specific dependencies into your codebase: the
rationale in giving you such options is because in certain scenarios it
might be just plain easier to read or code some specific piece of functionality
in such a way. The Spring Framework (almost) always offers you the choice
though: you have the freedom to make an informed decision as to which option
best suits your particular use case or scenario.
One such choice that is relevant to this chapter is that of which AOP
framework (and which AOP style) to choose. You have the choice of AspectJ and/or
Spring AOP, and you also have the choice of either the @AspectJ annotation-style
approach or the Spring XML configuration-style approach. The fact that this chapter
chooses to introduce the @AspectJ-style approach first should not be taken as
an indication that the Spring team favors the @AspectJ annotation-style approach
over the Spring XML configuration-style.
See the section entitled
Section 6.4, “Choosing which AOP declaration style to use”
for a fuller discussion
of the whys and wherefores of each style.
6.1.3. AOP Proxies
Spring AOP defaults to using standard J2SE dynamic proxies
for AOP proxies. This enables any interface (or set of interfaces) to be
proxied.
Spring AOP can also use CGLIB proxies. This is necessary to proxy
classes, rather than interfaces. CGLIB is used by default if a business
object does not implement an interface. As it is good practice to
program to interfaces rather than classes, business classes normally will
implement one or more business interfaces. It is possible to
force the use of CGLIB
,
in those (hopefully rare) cases where you need to advise a method that
is not declared on an interface, or where you need to pass a proxied object
to a method as a concrete type.
It is important to grasp the fact that Spring AOP is
proxy-based. See the section entitled
Section 6.6.1, “Understanding AOP proxies”
for a thorough examination
of exactly what this implementation detail actually means.
6.2. @AspectJ support
@AspectJ refers to a style of declaring aspects as regular Java classes annotated with
Java 5 annotations. The @AspectJ style was introduced by the
AspectJ project
as part of
the AspectJ 5 release. Spring 2.0 interprets the same annotations as AspectJ 5,
using a library supplied by AspectJ for pointcut parsing and matching. The AOP runtime
is still pure Spring AOP though, and there is no dependency on the AspectJ compiler
or weaver.
Using the AspectJ compiler and weaver enables use of the full AspectJ language,
and is discussed in
Section 6.8, “Using AspectJ with Spring applications”
.
6.2.1. Enabling @AspectJ Support
To use @AspectJ aspects in a Spring configuration you need to enable Spring
support for configuring Spring AOP based on @AspectJ aspects, and
autoproxying beans based on whether or not they are advised by
those aspects. By autoproxying we mean that if Spring determines that a bean is advised
by one or more aspects, it will automatically generate a proxy for that bean to intercept
method invocations and ensure that advice is executed as needed.
The @AspectJ support is enabled by including the following element inside
your spring configuration:
This assumes that you are using schema support as described in
Appendix A, XML Schema-based configuration
. See
Section A.2.6, “The aop schema”
for how to import the tags in the aop namespace.
If you are using the DTD, it is still possible to enable @AspectJ support by
adding the following definition to your application context:
You will also need two AspectJ libraries on the classpath of your application:
aspectjweaver.jar and
aspectjrt.jar. These libraries
are available in the 'lib' directory of an AspectJ installation (version
1.5.1 or later required), or in the 'lib/aspectj' directory of the
Spring-with-dependencies distribution.
6.2.2. Declaring an aspect
With the @AspectJ support enabled, any bean defined in your application context
with a class that is an @AspectJ aspect (has the @Aspect
annotation) will be automatically detected by Spring and used to configure Spring AOP.
The following example shows the minimal definition required for a not-very-useful
aspect:
A regular bean definition in the application context, pointing to a bean class
that has the @Aspect annotation:
And the NotVeryUsefulAspect class definition, annotated with
org.aspectj.lang.annotation.Aspect annotation;
package org.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class NotVeryUsefulAspect {
}
Aspects (classes annotated with @Aspect) may have
methods and fields just like any other class. They may also contain pointcut, advice,
and introduction (inter-type) declarations.
![]()
Advising aspects
In Spring AOP, it is not possible to
have aspects themselves be the target of advice from other aspects.
The @Aspect annotation on a class marks it as an
aspect, and hence excludes it from auto-proxying.
6.2.3. Declaring a pointcut
Recall that pointcuts determine join points of interest, and thus enable us
to control when advice executes. Spring AOP only supports method execution
join points for Spring beans, so you can think of a pointcut as matching
the execution of methods on Spring beans. A pointcut declaration has two parts: a
signature comprising a name and any parameters, and a pointcut expression
that determines exactly which method executions we are interested in.
In the @AspectJ annotation-style of AOP, a pointcut signature is provided by a regular method
definition, and the pointcut expression is indicated using the
@Pointcut annotation (the method serving as the pointcut
signature must have a void return
type).
An example will help make this distinction between a pointcut signature and a
pointcut expression clear. The following example defines a pointcut named
'anyOldTransfer' that will match the execution of any method named
'transfer':
@Pointcut("execution(* transfer(..))")// the pointcut expression
private void anyOldTransfer() {}// the pointcut signature
The pointcut expression that forms the value of the @Pointcut
annotation is a regular AspectJ 5 pointcut expression. For a full discussion of AspectJ's
pointcut language, see the
AspectJ Programming Guide
(and for Java 5 based extensions, the
AspectJ 5 Developers Notebook
)
or one of the books on AspectJ such as “Eclipse AspectJ” by Colyer et. al.
or “AspectJ in Action” by Ramnivas Laddad.
6.2.3.1. Supported Pointcut Designators
Spring AOP supports the following AspectJ pointcut designators for use in pointcut
expressions:
Other pointcut types
The full AspectJ pointcut language supports additional pointcut designators
that are not supported in Spring. These are: call, initialization,
preinitialization, staticinitialization, get, set, handler, adviceexecution,
withincode, cflow, cflowbelow, if, @this, and @withincode.
Use of these pointcut designators in pointcut expressions interpreted by Spring AOP
will result in an IllegalArgumentException being thrown.
The set of pointcut designators supported by Spring AOP may be extended in
future releases both to support more of the AspectJ pointcut designators (e.g. "if"),
and potentially to support Spring specific designators such as "bean" (matching on
bean name).
execution - for matching method execution join points,
this is the primary pointcut designator you will use when working with Spring AOP
within - limits matching to join points within certain
types (simply the execution of a method declared within a matching type when using
Spring AOP)
this - limits matching to
join points (the execution of methods when using Spring AOP) where the bean
reference (Spring AOP proxy) is an instance of the given type
target - limits matching to join points (the execution
of methods when using Spring AOP) where the target object (application object
being proxied) is an instance of the given type
args - limits matching to
join points (the execution of methods when using Spring AOP) where the
arguments are instances of the given types
@target - limits matching
to join points (the execution of methods when using Spring AOP) where the
class of the executing object has an annotation of the given type
@args - limits matching to
join points (the execution of methods when using Spring AOP) where the
runtime type of the actual arguments passed have annotations of the given type(s)
@within - limits matching
to join points within types that have the given annotation (the execution of methods
declared in types with the given annotation when using Spring AOP)
@annotation - limits matching to join points where the
subject of the join point (method being executed in Spring AOP) has the given
annotation
Because Spring AOP limits matching to only method execution join points, the
discussion of the pointcut designators above gives a narrower definition than you will
find in the AspectJ programming guide. In addition, AspectJ itself has type-based
semantics and at an execution join point both 'this' and 'target' refer to the same
object - the object executing the method. Spring AOP is a proxy based system and
differentiates between the proxy object itself (bound to 'this') and the target
object behind the proxy (bound to 'target').6.2.3.2. Combining pointcut expressions
Pointcut expressions can be combined using '&&', '||' and '!'. It is also possible
to refer to pointcut expressions by name. The following example shows three pointcut
expressions: anyPublicOperation (which matches if a method execution
join point represents the execution of any public method); inTrading (which matches
if a method execution is in the trading module), and tradingOperation
(which matches if a method execution represents any public method in the trading module).
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}
@Pointcut("within(com.xyz.someapp.trading..*")
private void inTrading() {}
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}
It is a best practice to build more complex pointcut expressions out of smaller
named components as shown above. When referring to pointcuts by name, normal Java
visibility rules apply (you can see private pointcuts in the same type, protected
pointcuts in the hierarchy, public pointcuts anywhere and so on). Visibility does not
affect pointcut matching.
6.2.3.3. Sharing common pointcut definitions
When working with enterprise applications, you often want to refer to modules of the
application and particular sets of operations from within several aspects. We recommend
defining a "SystemArchitecture" aspect that captures common pointcut expressions
for this purpose. A typical such aspect would look as follows:
package com.xyz.someapp;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class SystemArchitecture {
/**
* A join point is in the web layer if the method is defined
* in a type in the com.xyz.someapp.web package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.someapp.web..*)")
public void inWebLayer() {}
/**
* A join point is in the service layer if the method is defined
* in a type in the com.xyz.someapp.service package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.someapp.service..*)")
public void inServiceLayer() {}
/**
* A join point is in the data access layer if the method is defined
* in a type in the com.xyz.someapp.dao package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.someapp.dao..*)")
public void inDataAccessLayer() {}
/**
* A business service is the execution of any method defined on a service
* interface. This definition assumes that interfaces are placed in the
* "service" package, and that implementation types are in sub-packages.
*
* If you group service interfaces by functional area (for example,
* in packages com.xyz.someapp.abc.service and com.xyz.def.service) then
* the pointcut expression "execution(* com.xyz.someapp..service.*.*(..))"
* could be used instead.
*/
@Pointcut("execution(* com.xyz.someapp.service.*.*(..))")
public void businessService() {}
/**
* A data access operation is the execution of any method defined on a
* dao interface. This definition assumes that interfaces are placed in the
* "dao" package, and that implementation types are in sub-packages.
*/
@Pointcut("execution(* com.xyz.someapp.dao.*.*(..))")
public void dataAccessOperation() {}
}
The pointcuts defined in such an aspect can be referred to anywhere that you
need a pointcut expression. For example, to make the service layer transactional, you
could write:
The and
tags are discussed in the section entitled
Section 6.3, “Schema-based AOP support”
. The transaction
tags are discussed in the chapter entitled
Chapter 9, Transaction management
.
6.2.3.4. Examples
Spring AOP users are likely to use the execution pointcut
designator the most often. The format of an execution expression is:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
throws-pattern?)
All parts except the returning type pattern (ret-type-pattern in the snippet above),
name pattern, and parameters pattern are optional.
The returning type pattern determines what the return type of the method must be in order
for a join point to be matched. Most frequently you will use * as the
returning type pattern, which matches any return type. A fully-qualified type name will
match only when the method returns the given type. The name pattern matches the method name.
You can use the * wildcard as all or part of a name pattern. The
parameters pattern is slightly more complex: () matches a method that takes
no parameters, whereas (..) matches any number of parameters (zero or more).
The pattern (*) matches a method taking one parameter of any type,
(*,String) matches a method taking two parameters, the first can be of
any type, the second must be a String. Consult the
Language Semantics section of the AspectJ Programming Guide for more
information.
Some examples of common pointcut expressions are given below.
the execution of any public method:
execution(public * *(..))
the execution of any method with a name beginning with "set":
execution(* set*(..))
the execution of any method defined by the AccountService interface:
execution(* com.xyz.service.AccountService.*(..))
the execution of any method defined in the service package:
execution(* com.xyz.service.*.*(..))
the execution of any method defined in the service package or a sub-package:
execution(* com.xyz.service..*.*(..))
any join point (method execution only in Spring AOP) within the service package:
within(com.xyz.service.*)
any join point (method execution only in Spring AOP) within the service package or a sub-package:
within(com.xyz.service..*)
any join point (method execution only in Spring AOP) where the proxy implements the AccountService interface:
this(com.xyz.service.AccountService)'this' is more commonly used in a binding form :- see the following section
on advice for how to make the proxy object available in the advice body.
any join point (method execution only in Spring AOP) where the target object implements the AccountService interface:
target(com.xyz.service.AccountService)'target' is more commonly used in a binding form :- see the following section
on advice for how to make the target object available in the advice body.
any
join point (method execution only in Spring AOP) which takes a single
parameter, and where the argument passed at runtime is Serializable:
args(java.io.Serializable)'args' is more commonly used in a binding form :- see the following section
on advice for how to make the method arguments available in the advice body.
Note that the pointcut given in this example is different to
execution(* *(java.io.Serializable)): the args version matches if
the argument passed at runtime is Serializable, the execution version matches if the
method signature declares a single parameter of type Serializable.
any join point (method execution only in Spring AOP) where the target object has an @Transactional annotation:
@target(org.springframework.transaction.annotation.Transactional)'@target' can also be used in a binding form :- see the following section
on advice for how to make the annotation object available in the advice body.
any join point (method execution only in Spring AOP) where the declared type of the target object has an @Transactional annotation:
@within(org.springframework.transaction.annotation.Transactional)'@within' can also be used in a binding form :- see the following section
on advice for how to make the annotation object available in the advice body.
any join point (method execution only in Spring AOP) where the executing method has an @Transactional annotation:
@annotation(org.springframework.transaction.annotation.Transactional)'@annotation' can also be used in a binding form :- see the following section
on advice for how to make the annotation object available in the advice body.
any join point (method execution only in Spring AOP) which takes a single parameter, and where the
runtime type of the argument passed has the @Classified annotation:
@args(com.xyz.security.Classified)'@args' can also be used in a binding form :- see the following section
on advice for how to make the annotation object(s) available in the advice body.
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/24141/showart_324458.html |
|