从Spring2.0开始,Spring对AOP的支持增强了。使用起来更加方便,更加强大。
下面我们来看看新增的两种使用方式:@AspectJ风格和XML(schema)风格。
1.@AspectJ风格
首先通过实例来展示一下用法:
先看一下配置文件(aop_config_Annotation.xml):
接下来是我们定义的切面的具体实现(AspectExample.java),
其中的切入点定义,用到了另一个切面(SystemArchitecture.java)
AspectExample.java:
下面我们来看看新增的两种使用方式:@AspectJ风格和XML(schema)风格。
1.@AspectJ风格
首先通过实例来展示一下用法:
先看一下配置文件(aop_config_Annotation.xml):
xml
version
="1.0"
encoding
="UTF-8"
?>
< beans xmlns ="http://www.springframework.org/schema/beans"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
[url]http://www.springframework.org/schema/beans[/url]
[url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]
[url]http://www.springframework.org/schema/aop[/url]
[url]http://www.springframework.org/schema/aop/spring-aop-2.0.xsd[/url]" >
< aop:aspectj-autoproxy />
< bean id ="aAspectBean" class ="com.xyz.myapp.AspectExample" >
...
bean >
< bean id ="aTargetBean" class ="com.xyz.myapp.service.impl.AnImpledService" >
...
bean >
beans >
< beans xmlns ="http://www.springframework.org/schema/beans"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
[url]http://www.springframework.org/schema/beans[/url]
[url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]
[url]http://www.springframework.org/schema/aop[/url]
[url]http://www.springframework.org/schema/aop/spring-aop-2.0.xsd[/url]" >
< aop:aspectj-autoproxy />
< bean id ="aAspectBean" class ="com.xyz.myapp.AspectExample" >
...
bean >
< bean id ="aTargetBean" class ="com.xyz.myapp.service.impl.AnImpledService" >
...
bean >
beans >
其中的切入点定义,用到了另一个切面(SystemArchitecture.java)
AspectExample.java:
package com.xyz.myapp;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
@Aspect
//这个类使用了org.aspectj.lang.annotation.Aspect 注解,这个类被视作一个@Aspect切面
public class AspectExample {
//切面可能包括切入点,通知和引入(inter-type)声明。
//可以在同一个切面里定义多个通知,或者其他成员。
//前置通知(Before advice)--在一个匹配的方法执行前执行
@Before( "com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doAccessCheck() {
// ...
}
//使用一个in-place 的切入点表达式
@Before( "execution(* com.xyz.myapp.dao.*.*(..))")
public void doAccessCheck() {
// ...
}
//返回后通知(After returning advice)--在一个匹配的方法返回的时候执行
@AfterReturning( "com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doAccessCheck() {
// ...
}
//为了在通知体内得到返回的值使用了returning 属性
//在 returning 属性中使用的名字必须对应于通知方法内的一个参数名。
//当一个方法执行返回后,返回值作为相应的参数值传入通知方法。
//returning 子句也限制了匹配的返回值类型
//(在本例子中,返回值是 Object 类,也就是说返回任意类型都会匹配)
@AfterReturning(
pointcut= "com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
returning= "retVal")
public void doAccessCheck(Object retVal) {
// ...
}
//抛出后通知(After throwing advice)--在一个方法抛出异常后执行
@AfterThrowing( "com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doRecoveryActions() {
// ...
}
//限制通知只在某种特殊的异常被抛出的时候匹配,并可以在通知体内得到被抛出的异常。
//使用 throwing 属性不光可以限制匹配的异常类型(如果你不想限制,请使用 Throwable 作为异常类型),
//还可以将抛出的异常绑定到通知的一个参数上。
//在 throwing 属性中使用的名字必须与通知方法内的一个参数对应。 当一个方法因抛出一个异常而中止后,
//这个异常将会作为那个对应的参数送至通知方法。
//throwing 子句也限制了只能匹配到抛出指定异常类型的方法(在本例子中为 DataAccessException)。
@AfterThrowing(
pointcut= "com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
throwing= "ex")
public void doRecoveryActions(DataAccessException ex) {
// ...
}
//后通知(After (finally) advice)
//不论一个方法是如何结束的,在它结束后(finally)后通知(After (finally) advice)都会运行。
//这个通知必须做好处理正常返回和异常返回两种情况。通常用来释放资源。
@After( "com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doReleaseLock() {
// ...
}
//环绕通知(Around Advice)
//环绕通知在一个方法执行之前和之后执行。 它使得通知有机会既在一个方法执行之前又在执行之后运行。
//并且,它可以决定这个方法在什么时候执行,如何执行,甚至是否执行。
//环绕通知经常在在某线程安全的环境下,你需要在一个方法执行之前和之后共享某种状态的时候使用。
@Around( "com.xyz.myapp.SystemArchitecture.businessService()")
//通知的第一个参数必须是 ProceedingJoinPoint 类型。
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
//在通知体内,调用 ProceedingJoinPoint 的 proceed() 方法将会导致潜在的连接点方法执行。
//proceed 方法也可能会被调用并且传入一个 Object[] 对象-该数组将作为方法执行时候的参数。
//请注意proceed可能在通知体内部被调用一次,许多次,或者根本不被调用。
Object retVal = pjp.proceed();
// stop stopwatch
//方法的调用者得到的返回值就是环绕通知返回的值。
return retVal;
}
}
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
@Aspect
//这个类使用了org.aspectj.lang.annotation.Aspect 注解,这个类被视作一个@Aspect切面
public class AspectExample {
//切面可能包括切入点,通知和引入(inter-type)声明。
//可以在同一个切面里定义多个通知,或者其他成员。
//前置通知(Before advice)--在一个匹配的方法执行前执行
@Before( "com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doAccessCheck() {
// ...
}
//使用一个in-place 的切入点表达式
@Before( "execution(* com.xyz.myapp.dao.*.*(..))")
public void doAccessCheck() {
// ...
}
//返回后通知(After returning advice)--在一个匹配的方法返回的时候执行
@AfterReturning( "com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doAccessCheck() {
// ...
}
//为了在通知体内得到返回的值使用了returning 属性
//在 returning 属性中使用的名字必须对应于通知方法内的一个参数名。
//当一个方法执行返回后,返回值作为相应的参数值传入通知方法。
//returning 子句也限制了匹配的返回值类型
//(在本例子中,返回值是 Object 类,也就是说返回任意类型都会匹配)
@AfterReturning(
pointcut= "com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
returning= "retVal")
public void doAccessCheck(Object retVal) {
// ...
}
//抛出后通知(After throwing advice)--在一个方法抛出异常后执行
@AfterThrowing( "com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doRecoveryActions() {
// ...
}
//限制通知只在某种特殊的异常被抛出的时候匹配,并可以在通知体内得到被抛出的异常。
//使用 throwing 属性不光可以限制匹配的异常类型(如果你不想限制,请使用 Throwable 作为异常类型),
//还可以将抛出的异常绑定到通知的一个参数上。
//在 throwing 属性中使用的名字必须与通知方法内的一个参数对应。 当一个方法因抛出一个异常而中止后,
//这个异常将会作为那个对应的参数送至通知方法。
//throwing 子句也限制了只能匹配到抛出指定异常类型的方法(在本例子中为 DataAccessException)。
@AfterThrowing(
pointcut= "com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
throwing= "ex")
public void doRecoveryActions(DataAccessException ex) {
// ...
}
//后通知(After (finally) advice)
//不论一个方法是如何结束的,在它结束后(finally)后通知(After (finally) advice)都会运行。
//这个通知必须做好处理正常返回和异常返回两种情况。通常用来释放资源。
@After( "com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doReleaseLock() {
// ...
}
//环绕通知(Around Advice)
//环绕通知在一个方法执行之前和之后执行。 它使得通知有机会既在一个方法执行之前又在执行之后运行。
//并且,它可以决定这个方法在什么时候执行,如何执行,甚至是否执行。
//环绕通知经常在在某线程安全的环境下,你需要在一个方法执行之前和之后共享某种状态的时候使用。
@Around( "com.xyz.myapp.SystemArchitecture.businessService()")
//通知的第一个参数必须是 ProceedingJoinPoint 类型。
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
//在通知体内,调用 ProceedingJoinPoint 的 proceed() 方法将会导致潜在的连接点方法执行。
//proceed 方法也可能会被调用并且传入一个 Object[] 对象-该数组将作为方法执行时候的参数。
//请注意proceed可能在通知体内部被调用一次,许多次,或者根本不被调用。
Object retVal = pjp.proceed();
// stop stopwatch
//方法的调用者得到的返回值就是环绕通知返回的值。
return retVal;
}
}
SystemArchitecture.java:
package com.xyz.myapp;
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.myapp.web package or any sub-package
* under that.
*/
@Pointcut( "within(com.xyz.myapp.web..*)") // the pointcut expression
public void inWebLayer() {} // the pointcut signature
/**
* A join point is in the service layer if the method is defined
* in a type in the com.xyz.myapp.service package or any sub-package
* under that.
*/
@Pointcut( "within(com.xyz.myapp.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.myapp.dao package or any sub-package
* under that.
*/
@Pointcut( "within(com.xyz.myapp.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.myapp.abc.service and com.xyz.def.service) then
* the pointcut expression "execution(* com.xyz.myapp..service.*.*(..))"
* could be used instead.
*/
@Pointcut( "execution(* com.xyz.myapp.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.myapp.dao.*.*(..))")
public void dataAccessOperation() {}
}
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.myapp.web package or any sub-package
* under that.
*/
@Pointcut( "within(com.xyz.myapp.web..*)") // the pointcut expression
public void inWebLayer() {} // the pointcut signature
/**
* A join point is in the service layer if the method is defined
* in a type in the com.xyz.myapp.service package or any sub-package
* under that.
*/
@Pointcut( "within(com.xyz.myapp.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.myapp.dao package or any sub-package
* under that.
*/
@Pointcut( "within(com.xyz.myapp.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.myapp.abc.service and com.xyz.def.service) then
* the pointcut expression "execution(* com.xyz.myapp..service.*.*(..))"
* could be used instead.
*/
@Pointcut( "execution(* com.xyz.myapp.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.myapp.dao.*.*(..))")
public void dataAccessOperation() {}
}
@AspectJ风格的基本使用就如上面例子所示。
下面进行一些补充的说明:
切入点表达式:
这个很重要,为了灵活的设置我们的切面插入的位置,必须对它有足够的认识。
使用了AspectJ 切入点表达式语言。
JDK1.5及以上版本支持命名式切入点,使用命名切入点能够明显的提高代码的可读性。
更多信息参见:Spring参考手册--6.2.3. 声明一个切入点(pointcut)
切入点指定者的支持
其中最主要的切入点指定者是execution
合并切入点表达式
从更小的命名组件来构建更加复杂的切入点表达式是一种最佳实践。
共享常见的切入点(pointcut)定义
切入点表达式示例
通知参数(Advice parameters)
为了在通知(adivce)体内访问参数,需要进行参数绑定。
更多信息参见:Spring参考手册--6.2.4.6. 通知参数(Advice parameters)
通知(Advice)顺序
通过让切面的支持bean实现Ordered接口来指定优先级,进而控制通知的执行顺序。
更多信息参见:Spring参考手册--6.2.4.7. 通知(Advice)顺序
引入(Introductions)
为了让被通知对象实现一个给定的接口,应该使用引入。
更多信息参见:Spring参考手册--6.2.5. 引入(Introductions)
高级主题:切面实例化模型
默认情况下,在application context中每一个切面都会有一个实例。
AspectJ 把这个叫做单个实例化模型(singleton instantiation model)。
Spring还支持AspectJ的 perthis 和 pertarget 实例化模型。
更多信息参见:Spring参考手册--6.2.6. 切面实例化模型
-----------------------------------------------------------
下面进行一些补充的说明:
切入点表达式:
这个很重要,为了灵活的设置我们的切面插入的位置,必须对它有足够的认识。
使用了AspectJ 切入点表达式语言。
JDK1.5及以上版本支持命名式切入点,使用命名切入点能够明显的提高代码的可读性。
更多信息参见:Spring参考手册--6.2.3. 声明一个切入点(pointcut)
切入点指定者的支持
其中最主要的切入点指定者是execution
合并切入点表达式
从更小的命名组件来构建更加复杂的切入点表达式是一种最佳实践。
共享常见的切入点(pointcut)定义
切入点表达式示例
通知参数(Advice parameters)
为了在通知(adivce)体内访问参数,需要进行参数绑定。
更多信息参见:Spring参考手册--6.2.4.6. 通知参数(Advice parameters)
通知(Advice)顺序
通过让切面的支持bean实现Ordered接口来指定优先级,进而控制通知的执行顺序。
更多信息参见:Spring参考手册--6.2.4.7. 通知(Advice)顺序
引入(Introductions)
为了让被通知对象实现一个给定的接口,应该使用引入。
更多信息参见:Spring参考手册--6.2.5. 引入(Introductions)
高级主题:切面实例化模型
默认情况下,在application context中每一个切面都会有一个实例。
AspectJ 把这个叫做单个实例化模型(singleton instantiation model)。
Spring还支持AspectJ的 perthis 和 pertarget 实例化模型。
更多信息参见:Spring参考手册--6.2.6. 切面实例化模型
-----------------------------------------------------------
2.基于schema的风格
先看一下配置文件(aop_config_schema.xml):
先看一下配置文件(aop_config_schema.xml):
xml
version
="1.0"
encoding
="UTF-8"
?>
< beans xmlns ="http://www.springframework.org/schema/beans"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
[url]http://www.springframework.org/schema/beans[/url]
[url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]
[url]http://www.springframework.org/schema/aop[/url]
[url]http://www.springframework.org/schema/aop/spring-aop-2.0.xsd[/url]" >
< bean id ="aBean" class ="com.xyz.myapp.AspectExample2" >
...
bean >
< aop:config >
< aop:pointcut id ="businessService"
expression ="execution(* com.xyz.myapp.service.*.*(..))" />
< aop:pointcut id ="businessService"
expression ="com.xyz.myapp.SystemArchitecture.businessService()" />
< aop:aspect id ="myAspect" ref ="aBean" >
< aop:pointcut id ="businessService"
expression ="execution(* com.xyz.myapp.service.*.*(..))" />
< aop:before
pointcut-ref ="dataAccessOperation"
method ="doAccessCheck" />
< aop:before
pointcut ="execution(* com.xyz.myapp.dao.*.*(..))"
method ="doAccessCheck" />
< aop:after-returning
pointcut-ref ="dataAccessOperation"
method ="doAccessCheck" />
< aop:after-returning
pointcut-ref ="dataAccessOperation"
returning ="retVal"
method ="doAccessCheck" />
< aop:after-throwing
pointcut-ref ="dataAccessOperation"
method ="doRecoveryActions" />
< aop:after-throwing
pointcut-ref ="dataAccessOperation"
thowing ="dataAccessEx"
method ="doRecoveryActions" />
< aop:after
pointcut-ref ="dataAccessOperation"
method ="doReleaseLock" />
< aop:around
pointcut-ref ="businessService"
method ="doBasicProfiling" />
aop:aspect >
aop:config >
beans >
< beans xmlns ="http://www.springframework.org/schema/beans"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
[url]http://www.springframework.org/schema/beans[/url]
[url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]
[url]http://www.springframework.org/schema/aop[/url]
[url]http://www.springframework.org/schema/aop/spring-aop-2.0.xsd[/url]" >
< bean id ="aBean" class ="com.xyz.myapp.AspectExample2" >
...
bean >
< aop:config >
< aop:pointcut id ="businessService"
expression ="execution(* com.xyz.myapp.service.*.*(..))" />
< aop:pointcut id ="businessService"
expression ="com.xyz.myapp.SystemArchitecture.businessService()" />
< aop:aspect id ="myAspect" ref ="aBean" >
< aop:pointcut id ="businessService"
expression ="execution(* com.xyz.myapp.service.*.*(..))" />
< aop:before
pointcut-ref ="dataAccessOperation"
method ="doAccessCheck" />
< aop:before
pointcut ="execution(* com.xyz.myapp.dao.*.*(..))"
method ="doAccessCheck" />
< aop:after-returning
pointcut-ref ="dataAccessOperation"
method ="doAccessCheck" />
< aop:after-returning
pointcut-ref ="dataAccessOperation"
returning ="retVal"
method ="doAccessCheck" />
< aop:after-throwing
pointcut-ref ="dataAccessOperation"
method ="doRecoveryActions" />
< aop:after-throwing
pointcut-ref ="dataAccessOperation"
thowing ="dataAccessEx"
method ="doRecoveryActions" />
< aop:after
pointcut-ref ="dataAccessOperation"
method ="doReleaseLock" />
< aop:around
pointcut-ref ="businessService"
method ="doBasicProfiling" />
aop:aspect >
aop:config >
beans >
其中,切面的支持bean(AspectExample2)这个类跟我们在@AspectJ的例子中使用的AspectExample是相同的,
只是没有使用注解。切入点和通知信息通过XML配置来提供。
schema风格的基本使用就如上面例子所示。
下面进行一些补充的说明:
通知参数(Advice parameters)
参见:Spring参考手册6.3.3.6. 通知参数
通知(Advice)顺序
切面的优先级通过切面的支持bean是否实现了Ordered接口来决定。
引入(Introductions)
参见:Spring参考手册6.3.4. 引入
切面实例化模型
Schema-defined切面仅支持一种实例化模型就是singlton模型。
Advisors
参见:Spring参考手册6.3.6. Advisors
只是没有使用注解。切入点和通知信息通过XML配置来提供。
schema风格的基本使用就如上面例子所示。
下面进行一些补充的说明:
通知参数(Advice parameters)
参见:Spring参考手册6.3.3.6. 通知参数
通知(Advice)顺序
切面的优先级通过切面的支持bean是否实现了Ordered接口来决定。
引入(Introductions)
参见:Spring参考手册6.3.4. 引入
切面实例化模型
Schema-defined切面仅支持一种实例化模型就是singlton模型。
Advisors
参见:Spring参考手册6.3.6. Advisors