Spring对AOP的支持<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
4.1 AOP介绍
首先让我们从一些重要的AOP概念和术语开始。这些术语不是Spring特有的。不过AOP术语并不是特别的直观,如果Spring使用自己的术语,将会变得更加令人困惑。
·
切面(Aspect)
:一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在Spring AOP中,切面可以使用基于模式或者基于@Aspect注解的方式来实现。
·
连接点(Joinpoint)
:在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点总是表示一个方法的执行。
·
通知(Advice)
:在切面的某个特定的连接点上执行的动作。其中包括了“around”、“before”和“after”等不同类型的通知(通知的类型将在后面部分进行讨论)。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
·
切入点(Pointcut)
:匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。
·
引入(Introduction)
:用来给一个类型声明额外的方法或属性(也被称为连接类型声明(inter-type declaration))。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用引入来使一个bean实现
IsModified
接口,以便简化缓存机制。
·
目标对象(Target Object)
: 被一个或者多个切面所通知的对象。也被称做被通知(advised)对象。既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。
·
AOP
代理(AOP Proxy)
:AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
·
织入(Weaving)
:把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
通知类型:
- 前置通知(Before advice):在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
- 后置通知(After returning advice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
- 异常通知(After throwing advice):在方法抛出异常退出时执行的通知。
- 最终通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
- 环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。
环绕通知是最常用的通知类型。和AspectJ一样,Spring提供所有类型的通知,我们推荐你使用尽可能简单的通知类型来实现需要的功能。例如,如果你只是需要一个方法的返回值来更新缓存,最好使用后置通知而不是环绕通知,尽管环绕通知也能完成同样的事情。用最合适的通知类型可以使得编程模型变得简单,并且能够避免很多潜在的错误。比如,你不需要在JoinPoint上调用用于环绕通知的proceed()方法,就不会有调用的问题。在Spring 2.0中,所有的通知参数都是静态类型,因此你可以使用合适的类型(例如一个方法执行后的返回值类型)作为通知的参数而不是使用Object数组。 通过切入点匹配连接点的概念是AOP的关键,这使得AOP不同于其它仅仅提供拦截功能的旧技术。 切入点使得通知可以独立对应到面向对象的层次结构中。例如,一个提供声明式事务管理的环绕通知可以被应用到一组横跨多个对象的方法上(例如服务层的所有业务操作)。
4.2 创建通知
我们通过一个简单的例子来理解AOP。
代码清单1
public
class
Foo {
public
void
printName(String name){
System.
out
.println(
"The Name is : "
+ name);
}
public
void
printAge(String age){
System.
out
.println(
"The Age is : "
+ age);
}
}
import
java.lang.reflect.Method;
import
org.springframework.aop.MethodBeforeAdvice;
public
class
FooBeforeAdvice
implements
MethodBeforeAdvice{
@Override
public
void
before(Method method, Object[] args, Object object)
throws
Throwable {
//
打印出类的名称
System.
out
.print(
"Class Name is "
+
object.getClass().getSimpleName() +
" "
);
//
打印出参数的值
System.
out
.println(
"arg is "
+(String)args[0] +
" "
);
}
}
import
org.springframework.aop.framework.ProxyFactory;
public
class
Test {
public
static
void
main(String[] args) {
Foo foo =
new
Foo();
FooBeforeAdvice advice =
new
FooBeforeAdvice();
//Spring
提供的代理工厂
ProxyFactory pf =
new
ProxyFactory();
//
设置代理目标
pf.setTarget(
foo
);
//
为代理目标添加增强
pf.addAdvice(advice);
//
生成代理实例
Foo proxy = (Foo)pf.getProxy();
proxy.printName(
"Tony"
);
proxy.printAge(
"27"
);
}
}
控制台输出信息
Class Name is Foo arg is Tony
The Name is : Tony
Class Name is Foo arg is 27
The Age is : 27
代码清单
1
中我们的
Foo.java
类有两个方法,我们创建了
FooBeforeAdvice
继承前置增强接口
MethodBeforeAdvice
,在
before
方法中我同通过参数
object
获得代理的对象信息,通过参数
args
获得代理方法的参数值,最后我们通过
ProxyFactory
将目标类和增强类融合,生成了代理实例并调用代理实例的方法。而在
Spring
中又如何配置
AOP
呢?
4.3
前置增强
代码清单
2
<
bean
id
=
"foo"
class
=
"com.tony.test.Foo"
scope
=
"singleton"
/>
<
bean
id
=
"fooBeforeAdvice"
class
=
"com.tony.test.FooBeforeAdvice"
/>
<
bean
id
=
"proxy"
class
=
"org.springframework.aop.framework.ProxyFactoryBean"
>
<
property
name
=
"interceptorNames"
>
<!--
指定增强
-->
<
list
>
<
value
>
fooBeforeAdvice
</
value
>
</
list
>
</
property
>
<!--
指定目标代理
Bean -->
<
property
name
=
"target"
ref
=
"foo"
/>
</
bean
>
import
org.springframework.beans.factory.BeanFactory;
import
org.springframework.beans.factory.xml.XmlBeanFactory;
import
org.springframework.core.io.ClassPathResource;
public
class
Test {
public
static
void
main(String[] args) {
ClassPathResource resource =
new
ClassPathResource(
"spring-config-beans.xml"
);
//
实例化
BeanFactory
BeanFactory factory =
new
XmlBeanFactory(resource);
Foo foo = (Foo)factory.getBean(
"proxy"
);
foo.printName(
"Tony"
);
foo.printAge(
"27"
);
}
}
控制台信息
Class Name is Foo arg is Tony
The Name is : Tony
Class Name is Foo arg is 27
The Age is : 27
代码清单
2
中我们只需修改
Spring
的配置文件,将增强类和目标类装配起来就可以了,我们在
Test.java
就像正常的调用
Foo
一样,可是控制台取已经是被拦截了。
4.4
后置增强
代码清单
1
public
class
Foo {
public
String printName(String name){
return
"The Name is : "
+ name;
}
public
String printAge(String age){
return
"The Age is : "
+ age;
}
}
import
java.lang.reflect.Method;
import
org.springframework.aop.AfterReturningAdvice;
public
class
FooAfterAdvice
implements
AfterReturningAdvice{
@Override
//
参数分别是代理方法的返回值,被代理的方法,方法的参数,代理对象
public
void
afterReturning(Object returnValue, Method method,
Object[] args,Object object)
throws
Throwable {
//
打印出类的名称
System.
out
.print(
"Class Name is "
+
object.getClass().getSimpleName() +
" "
);
//
打印出参数的值
System.
out
.println(
"arg is "
+
(String)args[0] +
" "
);
//
打印出返回值图
System.
out
.println(
"ReturnValue is "
+
returnValue.toString());
}
}
<
bean
id
=
"foo"
class
=
"com.tony.test.Foo"
scope
=
"singleton"
/>
<
bean
id
=
"fooAfterAdvice"
class
=
"com.tony.test.FooAfterAdvice"
/>
<
bean
id
=
"proxy"
class
=
"org.springframework.aop.framework.ProxyFactoryBean"
>
<
property
name
=
"interceptorNames"
>
<!--
指定增强
-->
<
list
>
<
value
>
fooAfterAdvice
</
value
>
</
list
>
</
property
>
<!--
指定目标代理
Bean -->
<
property
name
=
"target"
ref
=
"foo"
/>
</
bean
>
控制台输出
Class Name is Foo arg is Tony
ReturnValue is The Name is : Tony
Class Name is Foo arg is 27
ReturnValue is The Age is : 27
代码清单中我们修改了
Foo.java
类两个方法都返回
String
类型的参数,定义了一个
FooAfterAdvice.java
类这个类实现了
AfterReturningAdvice
接口,分别打印出被代理类的名称,方法参数值和返回值。我们查看控制台输出的信息,发现在目标方法执行结束后还打印出增强类输出的信息。
4.5
环绕增强
代码清单
1
public
class
Foo {
public
void
printName(String name){
System.
out
.println(
"The Name is : "
+ name);
}
public
void
printAge(String age){
System.
out
.println(
"The Age is : "
+ age);
}
}
import
org.aopalliance.intercept.MethodInterceptor;
import
org.aopalliance.intercept.MethodInvocation;
public
class
FooInterceptor
implements
MethodInterceptor{
@Override
public
Object invoke(MethodInvocation invocation)
throws
Throwable {
//
目标方法入参
Object[] args = invocation.getArguments();
//
打印方法入参
System.
out
.println(
"
准备执行目标方法
"
);
//
执行目标方法
Object obj = invocation.proceed();
System.
out
.println(
"
目标方法执行完成
"
);
//
返回方法返回值
return
obj;
}
}
<
bean
id
=
"foo"
class
=
"com.tony.test.Foo"
scope
=
"singleton"
/>
<
bean
id
=
"fooInterceptor"
class
=
"com.tony.test.FooInterceptor"
/>
<
bean
id
=
"fooAfterAdvice"
class
=
"com.tony.test.FooAfterAdvice"
/>
<
bean
id
=
"proxy"
class
=
"org.springframework.aop.framework.ProxyFactoryBean"
>
<
property
name
=
"interceptorNames"
>
<!--
指定增强
-->
<
list
>
<
value
>
fooInterceptor
</
value
>
</
list
>
</
property
>
<!--
指定目标代理
Bean -->
<
property
name
=
"target"
ref
=
"foo"
/>
</
bean
>
控制台输出
准备执行目标方法
The Name is : Tony
目标方法执行完成
准备执行目标方法
The Age is : 27
目标方法执行完成
代码清单
1
中我们定义了
FooInterceptor.java
实现
MethodInterceptor
接口对目标方法进行环绕增强,查看控制台我们就能看出
FooInterceptor
在每次方法的执行前后都进行了处理。
我的其它Spring文章,也许会对您有帮助