1. 程式人生 > >Spring4-4 AOP配置

Spring4-4 AOP配置

  在Java社群中,最完整且最流行的AOP框架是AspectJ;而在Spring2.0以上版本中,可以使用基於AspectJ註解的方式或基於XML檔案的方式來配置AOP。

1. 基於AspectJ註解的方式配置AOP

1.1 實現步驟

  第一步:額外加入AspectJ所需要的jar包,具體如下圖所示:
    這裡寫圖片描述

  第二步:在Spring bean的配置檔案中,進行如下操作:

  • 加入aop、bean和context的名稱空間;
  • 配置自動掃描的包:如<context:component-scan base-package="com.qiaobc.spring.aop.impl"></context:component-scan>
  • 配置自動為匹配AspectJ註解的類生成代理物件:<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

.
  第三步:將橫切關注點的程式碼抽象到切面類中,即需要為切面類新增@Component和@Aspect註解,並在類中宣告通知。
  第四步:建立測試類並執行即可。

1.2 用AspectJ註解宣告切面

  這裡寫圖片描述

1.3 切入點表示式

  AspectJ的通知註解需要藉助切入點表示式來匹配目標方法,具體說明如下:
  這裡寫圖片描述

1.4 重用切入點

  這裡寫圖片描述

1.5 附:核心測試程式碼

package
com.qiaobc.spring.aop.impl; import java.util.Arrays; import java.util.List; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import
org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * 切面類:需要放入IoC容器中(@Component)、再宣告為一個切面(@Aspect) * 優先順序:可以使用@Order(1)註解來指定切面的優先順序,值越小優先順序越高 * 或 實現Ordered介面, getOrder()方法的返回值越小, 優先順序越高 * @author qiaobc */ @Aspect @Component public class LoggingAspect { /** * 宣告切入點表示式:@Pointcut、該方法不需要填入其他程式碼 * 引用切入點表示式:com.qiaobc.spring.aop.impl.LoggingAspect.declareJoinPointExpression() */ @Pointcut("execution(public int com.qiaobc.spring.aop.impl.ArithmeticCalculator.*(..))") private void declareJoinPointExpression(){} /** * 需求:在com.qiaobc.spring.aop.impl.ArithmeticCalculator介面的所有實現類的每個方法執行前列印日誌 * 前置通知:在目標方法開始之前執行,用@Before來宣告 * @param joinPoint : 用於讓通知訪問當前連線點的具體細節 */ @Before(value = "declareJoinPointExpression()") public void beforeMethod(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); List<Object> args = Arrays.asList(joinPoint.getArgs()); System.out.println("The method " + methodName + " begins with " + args); } /** * 後置通知:在目標方法執行之後執行(無論該方法是否出現異常),用@After來宣告 * 特別注意:在後置通知中無法訪問目標方法的執行結果 * @param joinPoint : 用於讓通知訪問當前連線點的具體細節 */ @After(value = "declareJoinPointExpression()") public void afterMethod(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println("The method " + methodName + " ends"); } /** * 返回通知:在目標方法正常結束後執行的通知,其可以訪問目標方法的返回值 * @param joinPoint : 用於讓通知訪問當前連線點的具體細節 * @param result : 用於獲取目標方法的返回值 */ @AfterReturning(value = "declareJoinPointExpression()", 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 exception : 用於獲取目標方法執行所丟擲的異常(該引數型別可以設定只有發生指定異常時才執行) */ @AfterThrowing(value = "declareJoinPointExpression()", throwing = "exception") public void afterThrowing(JoinPoint joinPoint, ArithmeticException exception) { String methodName = joinPoint.getSignature().getName(); System.out.println("Exception in method \"" + methodName + "\" " + exception); } /** * 環繞通知:類似於動態代理,其必須有返回值(目標方法的返回值) * @param proceedingJoinPoint : 環繞通知需要攜帶ProceedingJoinPoint型別的引數,用於決定是否執行目標方法 */ /* @Around (value = "execution(public int com.qiaobc.spring.aop.impl.ArithmeticCalculator.*(..))") public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint) { Object result = null; String methodName = proceedingJoinPoint.getSignature().getName(); List<Object> args = Arrays.asList(proceedingJoinPoint.getArgs()); // 執行目標方法 try { // 前置通知 System.out.println("The method " + methodName + " begins with " + args); // 執行目標方法 result = proceedingJoinPoint.proceed(); // 返回通知 System.out.println("The method " + methodName + " ends with " + result); } catch (Throwable e) { // 異常通知 System.out.println("Exception in method \"" + methodName + "\" " + e); // 問題:org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type ... throw new RuntimeException(e); } // 後置通知 System.out.println("The method " + methodName + " ends"); return result; } */ }

2. 基於XML檔案的方式配置AOP

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

    <!-- 配置bean -->
    <bean id="arithmeticCalculator" class="com.qiaobc.spring.aop.impl.xml.ArithmeticCalculatorImpl"></bean>

    <!-- 配置切面bean -->
    <bean id="loggingAspect" class="com.qiaobc.spring.aop.impl.xml.LoggingAspect"></bean>

    <!-- 配置AOP -->
    <aop:config>
        <!-- 配置切入點表示式 -->
        <aop:pointcut expression="execution(* com.qiaobc.spring.aop.impl.xml.ArithmeticCalculator.*(..))" id="logging"/>
        <!-- 配置切面及通知 -->
        <aop:aspect ref="loggingAspect" order="1">
            <aop:before method="beforeMethod" pointcut-ref="logging"/>
            <aop:after method="afterMethod" pointcut-ref="logging"/>
            <aop:after-returning method="afterReturning" returning="result" pointcut-ref="logging"/>
            <aop:after-throwing method="afterThrowing" throwing="exception" pointcut-ref="logging"/>
        </aop:aspect>
    </aop:config>

</beans>