Spring入門學習(AOP返回通知&異常通知&環繞通知&切面的優先順序) 第十六節
阿新 • • 發佈:2019-01-06
Spring入門學習(AOP返回通知&異常通知&環繞通知)
返回通知
使用`@AfterReturning`註解,在方法正常結束後執行的通知,它是可以獲得方法的返回值的。
- 在
LoggingAspect
類中新增如下方法:
使用/** * 在方法正常結束後執行的程式碼 * 返回通知是可以訪問到方法的返回值的 * @param joinPoint */ @AfterReturning(value="execution(* *.*(..))",returning=
@AfterReturning
註解,註解中的returning
獲取返回值。
測試結果:com.sun.proxy.$Proxy10 The methodadd begins...[3, 6] The method add ends... The method add ends with 9 result:9 The methoddiv begins...[6, 3] The method div ends... The method div ends with 2 result:2
異常通知
使用`@AfterThrowing`註解,在目標方法出現異常時會執行的程式碼,可以訪問到異常物件;且可以指定在出現特定異常時在執行通知程式碼
- 在
LoggingAspect
類中新增afterThrowing
方法:/** * 在目標方法出現異常時會執行的程式碼 * 可以訪問到異常物件;且可以指定在出現特定異常時在執行通知程式碼 * @param joinPoint * @param ex */ @AfterThrowing(value="execution(* *.*(..))",throwing="ex") public void afterThrowing
- 測試:
測試方法:
測試結果:public class Main { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:applicationContext.xml"); ArithmeticCalculator arithmeticCalculator = ctx.getBean(ArithmeticCalculator.class); System.out.println(arithmeticCalculator.getClass().getName()); int result = arithmeticCalculator.add(3, 6); System.out.println("result:" + result); result = arithmeticCalculator.div(6, 3); System.out.println("result:" + result); result = arithmeticCalculator.div(6, 0); System.out.println("result:" + result); } }
com.sun.proxy.$Proxy11 The methodadd begins...[3, 6] The method add ends... The method add ends with 9 result:9 The methoddiv begins...[6, 3] The method div ends... The method div ends with 2 result:2 The methoddiv begins...[6, 0] The method div ends... The method div occurs exception: java.lang.ArithmeticException: / by zero Exception in thread "main" java.lang.ArithmeticException: / by zero at com.fafa.spring.aop.impl.ArithmeticCalculatorImpl.div(ArithmeticCalculatorImpl.java:35) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:43) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) at com.sun.proxy.$Proxy11.div(Unknown Source) at com.fafa.spring.aop.impl.Main.main(Main.java:19)
環繞通知
- 使用
@Around
註解; - 環繞通知需要攜帶ProceedingJoinPoint型別的引數;
- 環繞通知類似於動態代理的全過程:ProceedingJoinPoint這個型別的引數可以決定是否執行目標方法;
- 且環繞通知必須有返回值,返回值即為有目標方法的返回值。
- 註釋掉
LoggingAspect
類中的其它的方法,新增如下方法:/** * 環繞通知需要攜帶ProceedingJoinPoint型別的引數。 * 環繞通知類似於動態代理的全過程:ProceedingJoinPoint這個型別的引數可以決定是否執行目標方法。 * 且環繞通知必須有返回值,返回值即為有目標方法的返回值 * @param proceedingJoinPoint */ @Around(value="execution(* *.*(..))") public Object aroundMethd(ProceedingJoinPoint pjd){ String methodName = pjd.getSignature().getName(); Object result = null; try { // 前置通知 System.out.println("The method "+methodName+ " begins"); result = pjd.proceed(); // 返回通知 System.out.println("The method "+methodName+" ends with "+result); } catch (Throwable e) { // 異常通知 System.out.println("The method "+methodName+ " occurs exception: "+e); e.printStackTrace(); } // 後置通知 System.out.println("The method "+methodName+ " ends "); return result; }
- 測試(去掉除0的):
可見環繞通知和動態代理是非常類似的,可以控制到方法是否執行,方法的返回值。com.sun.proxy.$Proxy8 The method add begins The method add ends with 9 The method add ends result:9 The method div begins The method div ends with 2 The method div ends result:2
切面的優先順序
- 此時我們再定義一個切面類
ValidationAspect
:@Component @Aspect public class ValidationAspect { @Before("execution(public int com.fafa.spring.aop.impl.ArithmeticCalculator.*(int,int))") public void validateArgs(JoinPoint joinPoint){ System.out.println("-->validate:"+Arrays.asList(joinPoint.getArgs())); } }
- 註釋掉
LoggingAspect
類中的aroundMethd
方法 - 執行main方法:
從結果看兩個切面類中com.sun.proxy.$Proxy10 The methodadd begins...[3, 6] -->validate:[3, 6] The method add ends... The method add ends with 9 result:9 The methoddiv begins...[6, 3] -->validate:[6, 3] The method div ends... The method div ends with 2 result:2
@Before
都執行了,但是我們如果要控制它們的順序該怎麼辦; - 此時可以在切面類上使用
@Order
註解指定切面的優先順序,值越小優先順序越高:@Component @Aspect @Order(1) public class ValidationAspect { @Before("execution(public int com.fafa.spring.aop.impl.ArithmeticCalculator.*(int,int))") public void validateArgs(JoinPoint joinPoint){ System.out.println("-->validate:"+Arrays.asList(joinPoint.getArgs())); } } // 把這個類宣告為一個切面: 需要把該類放入到IOC容器中,再宣告為一個切面 @Component @Aspect @Order(2) //使用Order指定切面的優先順序,值越小優先順序越高 public class LoggingAspect { // 宣告該方法是一個前置通知:在目標方法開始之前執行 // @Before("execution(public int com.fafa.spring.aop.impl.ArithmeticCalculator.add(int, int))") @Before("execution(* com.fafa.spring.aop.impl.*.*(int, int))") public void beforeMethod(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); List<Object> args = Arrays.asList(joinPoint.getArgs()); System.out.println("The method" + methodName +" begins..." + args); } // 後置通知就是在目標方法執行後(無論是否發生異常),都會執行的通知 // 在後置通知中還不能訪問目標方法執行的結果。 @After("execution(* com.fafa.spring.aop.impl.*.*(..))") public void afterMethod(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println("The method "+methodName+" ends..."); } /** * 在方法正常結束後執行的程式碼 * 返回通知是可以訪問到方法的返回值的 * @param joinPoint */ @AfterReturning(value="execution(* *.*(..))",returning="result") public void afterReturning(JoinPoint joinPoint, Object result){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method "+methodName+ " ends with "+result); } /** * 在目標方法出現異常時會執行的程式碼 * 可以訪問到異常物件;且可以指定在出現特定異常時在執行通知程式碼 * @param joinPoint * @param ex */ @AfterThrowing(value="execution(* *.*(..))",throwing="ex") public void afterThrowing(JoinPoint joinPoint, Exception ex){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method "+methodName+ " occurs exception: "+ex); } /** * 環繞通知需要攜帶ProceedingJoinPoint型別的引數。 * 環繞通知類似於動態代理的全過程:ProceedingJoinPoint這個型別的引數可以決定是否執行目標方法。 * 且環繞通知必須有返回值,返回值即為有目標方法的返回值 * @param proceedingJoinPoint */ /*@Around(value="execution(* *.*(..))") public Object aroundMethd(ProceedingJoinPoint pjd){ String methodName = pjd.getSignature().getName(); Object result = null; try { // 前置通知 System.out.println("The method "+methodName+ " begins"); result = pjd.proceed(); // 返回通知 System.out.println("The method "+methodName+" ends with "+result); } catch (Throwable e) { // 異常通知 System.out.println("The method "+methodName+ " occurs exception: "+e); e.printStackTrace(); } // 後置通知 System.out.println("The method "+methodName+ " ends "); return result; }*/ }
- 執行測試結果返回如下:
com.sun.proxy.$Proxy11 -->validate:[3, 6] The methodadd begins...[3, 6] The method add ends... The method add ends with 9 result:9 -->validate:[6, 3] The methoddiv begins...[6, 3] The method div ends... The method div ends with 2 result:2