Spring註解式開發(六):五分鐘搞定Spring AOP開發

AOP功能

AOP(Aspect Oriented Programming):面向切面編程 指在程序運行期間動態的將某段代碼切入到指定方法指定位置運行的編程方式
進行aop開發時首先要導入spring aop的依賴

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>

然後定義一個切面類,切面類裏面包含各種通知方法

  • 前置通知(@Before(“execution表達式”)):在目標方法運行之前執行
  • 後置通知(@After(“execution表達式”)):在目標方法運行之後執行
  • 異常通知(@AfterThrowing(“execution表達式”)):在目標方法出現異常時執行
  • 返回通知(@AfterReturning(“execution表達式”)):在目標方法又返回時執行
  • 環繞通知(@Around(“execution表達式”)):這個通知需要手動讓目標方法執行(joinPoint.procced)

execution表達式

基本語法如下
execution(<修飾符模式>?<返回類型模式><方法名模式>(<參數模式>)<異常模式>?) 除了返回類型模式、方法名模式和參數模式外,其它項都是可選的。
execution(* com.sample.service.impl….(…))

解釋如下:

符號 含義
execution() 表達式的主體
第一個”*“符號 表示返回值的類型任意;
com.sample.service.impl AOP所切的服務的包名,即,我們的業務部分
包名後面的”…“ 表示當前包及子包
第二個”*“ 表示類名,*即所有類。此處可以自定義,下文有舉例
.*(. .) 表示任何方法名,括號表示參數,兩個點表示任何參數類型

Spring Aop的使用

定義一個切面類,使用@Aspect表示這是一個切面類

/**
 * @Aspect註解告訴spring這是一個通知類
 */
@Aspect
public class MyAspect {
    /**
     * @Pointcut抽取公共的切入點
     */
    @Pointcut("execution(* com.wmq.spring.service.SpringService.print(..))")
    public void pointcut(){}
    //如果使用joinpoint,必須放在方法參數表的第一位
    @Before("pointcut()")
    public void before(JoinPoint joinPoint){
        //獲取方法簽名
        String name = joinPoint.getSignature().getName();
        System.out.println("before.........." + name);
        System.out.println("前置通知拿到的方法名" + name);
    }

    @After("execution(* com.wmq.spring.service.SpringService.print(..))")
    public void after(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        System.out.println("after..........");
        System.out.println("後置通知拿到的參數列表" +   Arrays.asList(args));
    }
    //獲取返回結果,使用result
    @AfterReturning(value = "pointcut()",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){
        System.out.println("afterReturning.........." + joinPoint.getSignature().getClass());
        System.out.println("返回通知拿到 的返回值" + result);
    }

    //獲取異常
    @AfterThrowing(value = "execution(* com.wmq.spring.service.SpringService.print(..))",throwing = "exception")
    public void afterThrowing(RuntimeException exception){
        System.out.println("afterThrowing..........");
        System.out.println("捕獲到的異常" + exception.getMessage());
    }

    @Around("execution(* com.wmq.spring.service.SpringService.print(..))")
    public void arouund(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around 目標方法執行之前");
        Object proceed = null;
        try {
            //通知方法執行
            proceed = joinPoint.proceed();
            System.out.println("around 環繞通知獲取的返回值" + proceed);
        } catch (Throwable throwable) {
            System.out.println("around 目標方法異常後執行");
        }
        System.out.println("arouund 目標方法執行之後" );
    }
}

定義一個業務類

public class SpringService {
    public Integer print(int a,int b){
        System.out.println("this is a service" + a + b);
        //if (a == 1) throw new RuntimeException("測試異常通知");
        return a + b;
    }
}

配置類,注意此處使用@EnableAspectJAutoProxy要開啓基於註解式的aop方式

@Configuration
/**
 * AOP 面向切面編程:只在程序運行期間動態的將某段代碼切入到指定方法指定位置運行的編程方式
 */
//開啓基於註解式的aop方式
@EnableAspectJAutoProxy
public class MyAopConfig {
    @Bean
    public SpringService springService(){
        return new SpringService();
    }
    @Bean
    public MyAspect myAspect(){
        return new MyAspect();
    }
}

測試

@Test
public void testAspect(){
    //創建一個applicationContext
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyAopConfig.class);
    SpringService bean = applicationContext.getBean(SpringService.class);
    bean.print(1,2);
}

沒有異常的結果

around 目標方法執行之前
before..........print
前置通知拿到的方法名print
this is a service12
around 環繞通知獲取的返回值3
arouund 目標方法執行之後
after..........
後置通知拿到的參數列表[1, 2]
afterReturning..........class org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint$MethodSignatureImpl
返回通知拿到 的返回值null

有異常的結果

around 目標方法執行之前
before..........print
前置通知拿到的方法名print
this is a service12
around 目標方法異常後執行:測試異常通知
arouund 目標方法執行之後
after..........
後置通知拿到的參數列表[1, 2]
afterReturning..........class org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint$MethodSignatureImpl
返回通知拿到 的返回值null

對比一下發現,當有環繞通知時目標方法發生異常時異常通知是不會執行的,但是這個地方爲什麼出現異常了返回通知還是執行

結束語

springaop的開發實際很簡單,但是對一門技術的學習我們不僅要學會怎麼使用,更重要的是,要知道原理,所以下一篇文章 將會介紹aop的原理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章