准备工作:目标类AdviceService, 注解方式的通知类MyAdvice, XML方式配置的通知类MyAdviceXML
XML配置所用的通知类 MyAdviceXML:
public class MyAdviceXML {
//前置通知
public void before() {
System.err.println("Before -- 前置通知 (XML配置)");
}
//后置通知
public void afterReturning() {
System.err.println("AfterReturning -- 后置通知 (XML配置)");
}
//异常通知
public void afterThrowing() {
System.err.println("AfterThrowing -- 异常通知 (XML配置)");
}
//最终通知
public void after() {
System.err.println("After -- 最终通知 (XML配置)");
}
//环绕通知
public Object around(ProceedingJoinPoint pjp) {
try {
System.err.println("Before -- 前置通知 (环绕通知 - XML配置)");
Object returnValue = pjp.proceed(pjp.getArgs());
System.err.println("AfterReturning -- 后置通知 (环绕通知 - XML配置)");
return returnValue;
} catch (Throwable e) {
System.err.println("AfterThrowing -- 异常通知 (环绕通知 - XML配置)抛出的异常:" + e);
throw (RuntimeException) e;
} finally {
System.err.println("After -- 最终通知 (环绕通知 - XML配置)");
}
}
}
XML对应的AOP配置:
<!--注册通知类-->
<bean id="myAdviceXML" class="com.zzq.util.MyAdviceXML"/>
<!--配置AOP-->
<aop:config>
<!--配置切入点表达式-->
<aop:pointcut id="pointcut" expression="execution(* com.zzq.service.impl.AdviceService.*(..))"/>
<!--配置切面-->
<aop:aspect id="myAdvice" ref="myAdviceXML">
<!--前置通知-->
<aop:before method="before" pointcut-ref="pointcut"/>
<!--后置通知-->
<aop:after-returning method="afterReturning" pointcut-ref="pointcut"/>
<!--异常通知-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut"/>
<!--最终通知-->
<aop:after method="after" pointcut-ref="pointcut"/>
<!--环绕通知-->
<aop:around method="around" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
注解配置的通知类 MyAdvice :
@Component
@Aspect
public class MyAdvice {
//通用切入点表达式
@Pointcut("execution(* com.zzq.service.impl.AdviceService.*(..))")
private void pointcut() {
}
//前置通知,同时获取切入点方法的形参
@Before(value = "pointcut() && args(a,b)", argNames = "a,b") //引用的切入点表达式后也可以直接追加 && args()
public void before(int a, int b) {
System.err.println("Before -- 前置通知 (注解配置)" + "方法参数:" + a + ", " + b);
}
//后置通知,同时获取方法返回值
@AfterReturning(value = "pointcut()", returning = "qwer")
public void afterReturning(Object qwer) {
System.err.println("AfterReturning -- 后置通知 (注解配置)" + "方法返回值:" + qwer);
}
//异常通知,同时获取方法抛出的异常
@AfterThrowing(value = "pointcut()", throwing = "asdf")
public void afterThrowing(Throwable asdf) {
System.err.println("AfterThrowing -- 异常通知 (注解配置)" + "抛出的异常:" + asdf);
}
//后置通知
@After("pointcut()")
public void after() {
System.err.println("After -- 最终通知 (注解配置)");
}
//环绕通知
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) {
try {
System.err.println("Before -- 前置通知 (环绕通知 - 注解配置)");
Object returnValue = pjp.proceed(pjp.getArgs());
System.err.println("AfterReturning -- 后置通知 (环绕通知 - 注解配置)");
return returnValue;
} catch (Throwable e) {
System.err.println("AfterThrowing -- 异常通知 (环绕通知 - 注解配置)抛出的异常:" + e);
throw (RuntimeException) e;
} finally {
System.err.println("After -- 最终通知 (环绕通知 - 注解配置)");
}
}
}
目标类:
@Service
public class AdviceService {
public String method(int a, int b) {
//int i=1/0; //制造异常
System.err.println("-----------------");
System.err.println("执行切入点方法...");
System.err.println("-----------------");
return (a + b) + "";
}
}
测试环境的配置类:
@Configuration
@ComponentScan("com.zzq")
@ImportResource("classpath:application.xml") //导入xml配置
@EnableAspectJAutoProxy(exposeProxy = true) //开启Spring注解AOP的支持
public class SpringConfiguration {
}
测试通知:
@Test
public void testAdvice() {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
context.getBean(AdviceService.class).method(1, 2);
}
无异常的结果:
Before -- 前置通知 (XML配置)
Before -- 前置通知 (环绕通知 - XML配置)
Before -- 前置通知 (环绕通知 - 注解配置)
Before -- 前置通知 (注解配置)方法参数:1, 2
-----------------
执行切入点方法...
-----------------
AfterReturning -- 后置通知 (注解配置)方法返回值:3
After -- 最终通知 (注解配置)
AfterReturning -- 后置通知 (环绕通知 - 注解配置)
After -- 最终通知 (环绕通知 - 注解配置)
AfterReturning -- 后置通知 (环绕通知 - XML配置)
After -- 最终通知 (环绕通知 - XML配置)
After -- 最终通知 (XML配置)
AfterReturning -- 后置通知 (XML配置)
Process finished with exit code 0
有异常的结果:
Before -- 前置通知 (XML配置)
Before -- 前置通知 (环绕通知 - XML配置)
Before -- 前置通知 (环绕通知 - 注解配置)
Before -- 前置通知 (注解配置)方法参数:1, 2
AfterThrowing -- 异常通知 (注解配置)抛出的异常:java.lang.ArithmeticException: / by zero
After -- 最终通知 (注解配置)
AfterThrowing -- 异常通知 (环绕通知 - 注解配置)抛出的异常:java.lang.ArithmeticException: / by zero
After -- 最终通知 (环绕通知 - 注解配置)
AfterThrowing -- 异常通知 (环绕通知 - XML配置)抛出的异常:java.lang.ArithmeticException: / by zero
After -- 最终通知 (环绕通知 - XML配置)
After -- 最终通知 (XML配置)
AfterThrowing -- 异常通知 (XML配置)
java.lang.ArithmeticException: / by zero
Process finished with exit code 0
总结:
以上列出了我们熟知的Spring通知的两种配置方式。其中需要我们注意的是:当我们使用注解配置通知时,最终通知的执行顺序与XML配置不同;XML配置的最终通知是在方法结束最终执行,而注解不同;
注解配置通知的执行顺序如下:
try{
try{
//@Before -- 首先执行前置通知
method.invoke(..); -- 然后执行切入点方法
}finally{
//@After -- 再而肯定会执行最终通知 --- 注解配置的注意点
}
//@AfterReturning -- 如果没有异常,则继续执行后置通知
return; -- 返回结果
}catch(){
//@AfterThrowing -- 如果有异常,则执行异常通知
}