Spring4 學習系列之——AOP的初解

在學習AOP以前,咱們先簡單理解下IOCAOP的原理。java

IOC:控制反轉編程

簡單明白什麼是控制反轉app

傳統的面向對象編程是在一個類裏須要哪一個對象就new一個,new多了,就會形成各個類彼此之間的依賴性很高,甚至會形成一個對象出了個問題,致使其餘類對象都不能運行的狀況。因此這就是所謂的耦合度很高。ide

那麼確定須要一個方法來解耦啦,那就是ioc,ioc它是一個容器,在容器裏存放的是bean對象,那麼一個類要成爲ioc內置的bean對象,須要在java類裏面加入相關的註解聲明,有了這個聲明,ioc容器加載時就會全權管理這個類對象的一切,包括生命週期,也能夠理解ioc是beanFactory的升級版,咱們知道,beanFactory一般是在須要的類裏直接靜態或者繼承關係來得到beanFactory裏面的對象或者方法的,這種方法的依賴性仍是有的,那麼做爲它的升級版ioc,那就不同了,它能夠說徹底不依賴任何java文件,而是哪一個java類須要某個對象,直接注入提供給他,極大的下降了耦合度。學習

DI:依賴注入。其實和IOC是一個原理,它只是基於IOC思想的一個實現測試

依賴注入有三種方法spa

  1. set方法
  2. 構造器
  3. 接口(不推薦)

依賴注入很簡單,不作描述,通常都是配置自動掃描,而後自動注入@autowrite代理

 

AOP:面向切面日誌

說實話也是ioc思想的衍生,面向對象完善,是一個很值得研究深透的一個思想。具體東西不說,直接簡單點解釋。在開發中,好比要作一個日誌監控功能,功能要求每一個方法先後都要後臺打印執行的開始,結束時間,咱們能夠寫一個接口或者繼承來實現,但總管這樣,效率依舊很低。咱們能夠這樣子想:既然這些方法都要一個日誌功能,傳統的面向接口不太好,那麼咱們就用aop面向切面吧! 把日誌功能當成一個切面,而這個切面是由許多切點構成的,想一下:不少個點,而後就成了一個面。。可是這些點的業務需求是同樣的,只是實現不同,不然有其餘的點混入進來,這個「面」就不完美了,不完美那就不提倡了。那麼什麼是點呢?點就是鏈接點,一個方法裏先後加入日誌功能,那麼它就有兩個點,一前一後,這兩個點咱們就能夠在抽出來的那個所謂的 "面 "的類中插入日誌功能,咱們能夠經過註解或者xml來插入,有5種插入順序,下面的列子會看到,其內部經過java的動態代理來實現,好,上個經典的例子。。component

 

配置文件

        <!-- 自動掃描 -->
        <context:component-scan base-package="aop"></context:component-scan>
        <!-- 自動代理 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 

有兩個類:一個接口:ArithmeticCalculator     一個實現:ArithmeticCalculatorImpl

接口定義了簡單的加減乘除

public interface ArithmeticCalculator {

    int add(int i, int j);
    int sub(int i, int j);
    
    int mul(int i, int j);
    int div(int i, int j);
    
}

接口的實現,返回結果

@Component
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

    @Override
    public int add(int i, int j) {
        int result = i + j;
        return result;
    }

    @Override
    public int sub(int i, int j) {
        int result = i - j;
        return result;
    }

    @Override
    public int mul(int i, int j) {
        int result = i * j;
        return result;
    }

    @Override
    public int div(int i, int j) {
        int result = i / j;
        return result;
    }

}
 

接下來  我要在每一個方法裏先後,或者出現異常的時候都打印日誌,由於這些功能都是相同的,並且每一個方法裏都有接入點,因此就組成一個切面類:AspectjTest.class

@Component // 加入到ioc容器中,讓其自動掃描
@Aspect // 聲明爲一個切面
public class AspectjTest {
    // 前置通知,在目標方法開始前執行
    @Before(value = "execution(int aop.ArithmeticCalculatorImpl.*(int, int))")
    public void beforeMthod(JoinPoint joinpoint) {
        String methodName = joinpoint.getSignature().getName();
        Object[] args = joinpoint.getArgs();
        List<Object> largs = (List) Arrays.asList(args);
        System.out.println("the methood " + methodName + " begins with " + largs);
    }
    
    //後置通知,在目標方法執行完成後,不管結果有沒有異常,都會執行的一個通知
    //在後置通知裏還不能訪問目標方法的結果
    @After(value="execution(int aop.ArithmeticCalculatorImpl.*(int, int))")
    public void afterMethod(JoinPoint joinpoint){
        String methodName = joinpoint.getSignature().getName();
        System.out.println("the methood " + methodName + " ends");
    }
    
    //運行結束通知,在目標方法正常執行完成後的一個通知
    //能夠訪問到返回結果
    @AfterReturning(value="execution(int aop.ArithmeticCalculatorImpl.*(int, int))",returning="result")
    public void afterReturnRunMethod(JoinPoint joinPoint,Object result){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("the methood " + methodName + " ends with: "+result);
    }
    
    //異常通知,在目標方法執行時發生異常返回的一個通知
    //能夠訪問到異常結果
    @AfterThrowing(value="execution(int aop.ArithmeticCalculatorImpl.*(int, int))",throwing="expection")
    public void afterThrowingMethod(JoinPoint joinPoint,Object expection){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("the methood "+methodName+" throw: "+expection    );
    }
    
    //環繞通知:需攜帶ProceedingJoinPoint類型的參數
    //環繞通知相似動態代理的全過程,ProceedingJoinPoint 類型的參數能夠決定是否執行目標方法
    //環繞通知必須有返回值,返回值爲目標方法的返回值
    
    @Around(value="execution(int aop.ArithmeticCalculatorImpl.*(int, int))")
    public Object aroundMethod(ProceedingJoinPoint pjp){
        String methodName = pjp.getSignature().getName();
        Object result = null;
        try {
            //前置通知
            System.out.println("the methood " + methodName + " begins with " +Arrays.asList(pjp.getArgs()));
            //執行目標方法
            result = pjp.proceed();
            //運行結束通知(正常結束)
            System.out.println("the methood " + methodName + " ends with: "+result);
        } catch (Throwable e) {
            //異常通知
            System.out.println("the methood "+methodName+" throw: "+e);
        }
        
        //後置通知(無論有沒有異常都會返回)
        System.out.println("the methood " + methodName + " ends");
        return result;
    }
}
 

客戶端測試下:

public class Main {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
        ArithmeticCalculator ar = (ArithmeticCalculator) ctx.getBean("arithmeticCalculatorImpl");
        int result = ar.add(1, 2);
        System.out.println("result:"+result);
        
        int result2 = ar.div(3, 1);
        System.out.println("result2:"+result2);
    }

}
 

觀察控制檯,結合上述,便能理解AOP的美妙之處

相關文章
相關標籤/搜索