spring aop xml风格

Warning

<aop:config>风格的配置使得Spring auto-proxying机制的使用变得很笨重。如果你已经通过 BeanNameAutoProxyCreator或类似的东西显式使用auto-proxying,它可能会导致问题 (例如通知没有被织入)。 推荐的使用模式是仅仅使用<aop:config>风格, 或者仅仅使用AutoProxyCreator风格。

声明一个切面
有了schema的支持,切面就和常规的Java对象一样被定义成application context中的一个bean。   对象的字段和方法提供了状态和行为信息,XML文件则提供了切入点和通知信息。

切面使用<aop:aspect>来声明,backing bean(支持bean)通过 ref 属性来引用:

声明一个切入点
一个命名切入点可以在<aop:config>元素中定义,这样多个切面和通知就可以共享该切入点。
一个描述service层中所有service执行的切入点可以定义如下:
<aop:config>
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
</aop:config>

几乎和@AspectJ切面中的一样,使用基于schema定义风格声明的切入点可以收集(collect) 连接点上下文。例如,下面的切入点收集'this'对象作为连接点上下文并传递它给通知:
<aop:config>

<aop:aspect id="myAspect" ref="aBean">

<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..)) && this(service)"/>
<aop:before pointcut-ref="businessService" method="monitor"/>
...

</aop:aspect>

</aop:config>
通过包含匹配名字的参数,通知被声明来接收收集的连接点上下文:
public void monitor(Object service) {
...
}
当需要连接子表达式的时候,'&&'在XML中用起来非常不方便,所以关键字'and', 'or' 和 'not'可以分别用来代替'&&', '||' 和 '!'。例如,上面切入点更好的写法如下:
<aop:config>

<aop:aspect id="myAspect" ref="aBean">

<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..)) and this(service)"/>
<aop:before pointcut-ref="businessService" method="monitor"/>
...

</aop:aspect>

</aop:config>

声明通知
 

和@AspectJ风格一样,基于schema的风格也支持5种通知类型并且两者具有同样的语义。
前置通知
前置通知在匹配方法执行前运行。在<aop:aspect>中使用<aop:before> 元素来声明它。

<aop:aspect id="beforeExample" ref="aBean">

<aop:before
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>

...

</aop:aspect>

这里dataAccessOperation是一个顶级(<aop:config>)切入点的id。    而要定义内置切入点,需将pointcut-ref属性替换为pointcut属性:

<aop:aspect id="beforeExample" ref="aBean">

<aop:before
pointcut="execution(* com.xyz.myapp.dao.*.*(..))"
method="doAccessCheck"/>

...

</aop:aspect>
正如我们在@AspectJ风格章节中讨论过的,使用命名切入点能够明显的提高代码的可读性。

Method属性标识了提供通知主体的方法(doAccessCheck)。 这个方法必须定义在包含通知的切面元素所引用的bean中。在一个数据访问操作执行之前 (一个方法执行由切入点表达式所匹配的连接点),切面中的"doAccessCheck"会被调用。
后置通知

后置通知在匹配的方法完全执行后运行。和前置通知一样,可以在<aop:aspect> 里面声明它。例如:

<aop:aspect id="afterReturningExample" ref="aBean">

<aop:after-returning
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>

...

</aop:aspect>

和@AspectJ风格一样,通知主体可以得到返回值。使用returning属性来指定传递返回值的参数名:

<aop:aspect id="afterReturningExample" ref="aBean">

<aop:after-returning
pointcut-ref="dataAccessOperation"
returning="retVal"
method="doAccessCheck"/>

...

</aop:aspect>

异常通知

异常通知在匹配方法抛出异常退出时执行。在<aop:aspect>中使用 after-throwing元素来声明:

<aop:aspect id="afterThrowingExample" ref="aBean">

<aop:after-throwing
pointcut-ref="dataAccessOperation"
method="doRecoveryActions"/>

...

</aop:aspect>

和@AspectJ风格一样,通知主体可以得到抛出的异常。使用throwing属性来指定传递异常的参数名:

<aop:aspect id="afterThrowingExample" ref="aBean">

<aop:after-throwing
pointcut-ref="dataAccessOperation"
throwing="dataAccessEx"
method="doRecoveryActions"/>

...

</aop:aspect>

doRecoveryActions方法必须声明一个名字为dataAccessEx的参数。 参数的类型依照@AfterThrowing所描述的方法强制匹配。例如:方法签名可以如下这般声明:

public void doRecoveryActions(DataAccessException dataAccessEx) {...
doAccessCheck方法必须声明一个名字叫 retVal 的参数。 参数的类型依照@AfterReturning所描述的方法强制匹配。例如,方法签名可以这样声明:

public void doAccessCheck(Object retVal) {...


最终通知

最终通知无论如何都会在匹配方法退出后执行。使用after元素来声明它:

<aop:aspect id="afterFinallyExample" ref="aBean">

<aop:after
pointcut-ref="dataAccessOperation"
method="doReleaseLock"/>

...

</aop:aspect>

环绕通知

环绕通知是最后一种通知类型。环绕通知在匹配方法运行期的“周围”执行。 它有机会在目标方法的前面和后面执行,并决定什么时候运行,怎么运行,甚至是否运行。 环绕通知经常在需要在一个方法执行前后共享状态信息,并且是在线程安全的情况下使用 (启动和停止一个计时器就是一个例子)。注意选择能满足你需求的最简单的通知类型; 如果简单的前置通知能做的事情就绝对不要使用环绕通知。

Around通知使用aop:around元素来声明。通知方法的第一个参数的类型必须是 ProceedingJoinPoint类型。在通知的主体中,调用 ProceedingJoinPoint的proceed()方法来执行真正的方法。 proceed方法也可能会被调用并且传入一个Object[]对象 - 该数组将作为方法执行时候的参数。参见Section 6.2.4.5, “环绕通知”中调用具有 Object[]的proceed方法。

<aop:aspect id="aroundExample" ref="aBean">

<aop:around
pointcut-ref="businessService"
method="doBasicProfiling"/>

...

</aop:aspect>

doBasicProfiling通知的实现和@AspectJ中的例子完全一样(当然要去掉注解):

public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}

通知参数

Schema-based声明风格和@AspectJ一样,支持多种类型的通知:通过通知方法参数名字来匹配切入点参数。 参见Section 6.2.4.6, “通知参数(Advice parameters)”获取详细信息。如果你希望显式指定通知方法的参数名 (而不是依靠先前提及的侦测策略),可以通过通知元素的arg-names属性来实现,它的处理和 在Section 6.2.4.6.3, “确定参数名”中所描述的对通知注解中"argNames"属性的处理方式一样。 示例如下:

<aop:before
pointcut="com.xyz.lib.Pointcuts.anyPublicMethod() and @annotation(auditable)"
method="audit"
arg-names="auditable"/>

arg-names属性接受由逗号分割的参数名列表。

下面是个稍微复杂的基于XSD的例子,它展示了关联了多个强类型参数的环绕通知的使用。

package x.y.service;

public interface FooService {

Foo getFoo(String fooName, int age);
}

public class DefaultFooService implements FooService {

public Foo getFoo(String name, int age) {
return new Foo(name, age);
}
}

接下来看切面。注意profile(..)方法接受多个强类型参数, 首先连接点在方法调用时执行,这个参数指明profile(..)会被用作 环绕通知:

package x.y;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;

public class SimpleProfiler {

public Object profile(ProceedingJoinPoint call, String name, int age) throws Throwable {
StopWatch clock = new StopWatch(
"Profiling for '" + name + "' and '" + age + "'");
try {
clock.start(call.toShortString());
return call.proceed();
} finally {
clock.stop();
System.out.println(clock.prettyPrint());
}
}
}

最后这里是使得上面的通知针对一个特定连接点而执行所必需的XML配置:

<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="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<!-- this is the object that will be proxied by Spring's AOP infrastructure -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>

<!-- this is the actual advice itself -->
<bean id="profiler" class="x.y.SimpleProfiler"/>

<aop:config>
<aop:aspect ref="profiler">

<aop:pointcut id="theExecutionOfSomeFooServiceMethod"
expression="execution(* x.y.service.FooService.getFoo(String,int))
and args(name, age)"/>

<aop:around pointcut-ref="theExecutionOfSomeFooServiceMethod"
method="profile"/>

</aop:aspect>
</aop:config>

</beans>

如果我们有下面的驱动脚本,我们将在标准输出上得到如下的输出:

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import x.y.service.FooService;

public final class Boot {

public static void main(final String[] args) throws Exception {
BeanFactory ctx = new ClassPathXmlApplicationContext("x/y/plain.xml");
FooService foo = (FooService) ctx.getBean("fooService");
foo.getFoo("Pengo", 12);
}
}

通知顺序

当同一个切入点(执行方法)上有多个通知需要执行时,执行顺序的规则如 Section 6.2.4.7, “通知顺序”所述。切面的优先级通过给切面的支持bean增加 Order注解或者使切面的支持bean实现 Ordered接口来决定。   

引入

引入(在AspectJ中称为inter-type声明)允许一个切面声明一个通知对象实现指定接口, 并且提供了一个接口实现类来代表这些对象。

引入的定义使用aop:aspect中的aop:declare-parents元素。 该元素用于声明所匹配的类型有一个新的父类型(所以有了这个名字)。 例如,给定接口UsageTracked, 以及这个接口的一个实现类 DefaultUsageTracked, 下面的切面声明所有实现service接口的类同时实现 UsageTracked 接口。(比如为了通过JMX输出统计信息)

<aop:aspect id="usageTrackerAspect" ref="usageTracking">

<aop:declare-parents
types-matching="com.xzy.myapp.service.*+"
implement-interface="com.xyz.myapp.service.tracking.UsageTracked"
default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked"/>

<aop:before
pointcut="com.xyz.myapp.SystemArchitecture.businessService()
and this(usageTracked)"
method="recordUsage"/>

</aop:aspect>

usageTracking bean的支持类可以包含下面的方法:

public void recordUsage(UsageTracked usageTracked) {
usageTracked.incrementUseCount();
}

要实现的接口由implement-interface属性来指定。  types-matching属性的值是一个AspectJ类型模式:任何匹配类型的bean都会实现  UsageTracked 接口。注意在上面前置通知的例子中, serevice bean可以直接用作UsageTracked接口的实现。 如果以编程形式访问一个bean,你可以这样来写:

UsageTracked usageTracked = (UsageTracked) context.getBean("myService");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值