转载:Spring入门(Schema-based AOP其四)
Introduction
简介允许切面声明一个实现指定接口的通知对象,并且提供了一个接口实现类来表达这些对象
由<aop:aspect>
中的<aop:declare-parents>
元素声明。该元素用于声明所匹配的类型拥有一个新的parent(因此得名)
<aop:aspect id="usageTrackerAspect" ref="usageTracking">
<aop:declare-parents
<!--匹配什么样的类型-->
type-matching="com.xyz.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>
public void recordUsage(UsageTracked usageTracked){
usageTracked.incrementUseCount();
}
这是一个方法,方法里边输入是一个接口,然后调用这个接口的incrementUseCount方法,增加被调用的次数。
UsageTracked usageTracked=(UsageTracked)context.getBean("myService");
matching匹配到的是myService这个类,也就是bean的id是myService,但是我们getBean之后会把它强制转为这个接口,为什么能够这么转?因为刚才有说到,<aop:declare-parents>
会用于声明所有匹配的类型拥有一个新的parent,也就是说,当前这个declare-parents匹配到了myService这个类,所以它会给myService这个bean对应的类指定一个新的parent,也就是implement-interface里边指定的UsageTracked,所以这里可以做这种强制类型转化。
示例
//com.imooc.aop.schema.advice包下
public interface Fit {
void filter();
}
public class FitImpl implements Fit {
@Override
public void filter() {
System.out.println("FitImpl filter.");
}
}
<aop:config>
<aop:aspect id="moocAspectAOP" ref="moocAspect">
<!--types-matching匹配的是com.imooc.aop.schema.advice.biz这个包下的所有方法。-->
<aop:declare-parents
types-matching="com.imooc.aop.schema.advice.biz.*(+)"
implement-interface="com.imooc.aop.schema.advice.Fit"
default-impl="com.imooc.aop.schema.advice.FitImpl"/>
</aop:aspect>
</aop:config>
@Test
public void testFit() {
Fit fit = (Fit)super.getBean("aspectBiz");
fit.filter();
}
//输出FitImpl filter.
也就是说Fit接口的实现类FitImpl已经被调用,types-matching匹配到AspectBiz这个类,为它指定了一个新的父类Fit,它的实现FitImpl。
advisor
advisor就像一个小的自包含的方面,只有一个advice
切面自身通过一个bean表示,并且必须实现某个advice接口,同时,advisor也可以很好的利用AspectJ的切入点表达式 。
Spring通过配置文件中<aop:advisor>
元素支持advisor。
实际使用中,大多数情况下它会和transactional advice配合使用
(也就是使用时,会和与事务相关的advice使用) 为了定义一个advisor的优先级以便让advice可以有序,可以使用order属性来定义advisor的顺序
<aop:config>
<aop:pointcut id="businessService"
expression="execution(*com.xyz.myapp.service..(..))"/>
<aop:advisor
pointcut-ref="businessService"
advice-ref="tx-advice"/>
</aop:config>
<tx:advice id="tx-advice">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
tx:advice是一个关于事务的相关的声明。
<context:component-scan base-package="com.imooc.aop.schema"></context:component-scan>
<aop:config>
<aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor">
<aop:pointcut id="idempotentOperation"
expression="execution(* com.imooc.aop.schema.advisors.service.*.*(..)) " />
<!-- expression="execution(* com.imooc.aop.schema.service.*.*(..)) and -->
<!-- @annotation(com.imooc.aop.schema.Idempotent)" /> -->
<aop:around pointcut-ref="idempotentOperation" method="doConcurrentOperation" />
</aop:aspect>
</aop:config>
<bean id="concurrentOperationExecutor" class="com.imooc.aop.schema.advisors.ConcurrentOperationExecutor">
<property name="maxRetries" value="3" />
<property name="order" value="100" />
</bean>
context:component-scan是一个自动的扫描,扫描包下面的配置
然后看一下bean对应的类ConcurrentOperationExecutor,这个类作为一个切面来使用
public class ConcurrentOperationExecutor implements Ordered {
private static final int DEFAULT_MAX_RETRIES = 2;
private int maxRetries = DEFAULT_MAX_RETRIES;
private int order = 1;
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
//对应切面中的环绕通知方法
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
int numAttempts = 0;
PessimisticLockingFailureException lockFailureException;
do {
numAttempts++;
System.out.println("Try times : " + numAttempts);
try {
return pjp.proceed();
} catch (PessimisticLockingFailureException ex) {
lockFailureException = ex;
}
} while (numAttempts <= this.maxRetries);
System.out.println("Try error : " + numAttempts);
throw lockFailureException;
}
}
匹配到的类是InvokeService
@Service
public class InvokeService {
public void invoke() {
System.out.println("InvokeService ......");
}
public void invokeException() {
throw new PessimisticLockingFailureException("");
}
}
这个类里边有两个方法,invoke是可以正常调用。invokeException每一次调用都会抛出异常,所以每次执行都会失败,超过最大执行次数会抛出lockFailureException并打印。
单元测试方法
@Test
public void testSave() {
InvokeService service = super.getBean("invokeService");
service.invoke();
System.out.println();
service.invokeException();
}