1. 程式人生 > >AOP概念與執行原理

AOP概念與執行原理

引言:

AOP指的就是面向切面程式設計,在實際的開發和工作中很多地方都深有體現,比如許可權控制,控制全域性狀態等。接下來會詳細闡述AOP的概念,給出對應的DEMO來深入學習實踐,探討AOP的意義。

技術點:
1、反射(reflect)

在執行狀態中,對於任意一個類,都能夠知道這個類的屬性和方法。對於任意一個物件,都能呼叫它的任意一個方法和屬性(private的方法也可以呼叫,不用覺得封裝就變得沒有意義了,筆者覺得存在就是合理的)。這種動態獲取的資訊以及動態呼叫物件的方法稱為反射。

PS:順便提一個有意思的情況,筆者親身試驗的:比如說在Integer的List中放置一個String是否可以實現呢?在反射就可以實現哦,下面是實現的程式碼:

            ArrayList<Integer> list = new ArrayList<Integer>();
            Method method = list.getClass().getMethod("add", Object.class);
            method.invoke(list, "我是String");
            System.out.println(list.get(0));

            //列印結果:我是String

以下是反射的一般作用:

  • 在執行時判斷任意一個物件所屬的類

  • 在執行時構造任意一個類的物件

  • 在執行時判斷一個類所具有的成員變數和方法

  • 在執行時呼叫任意一個物件的方法

  • 生成動態代理

面向切面程式設計(AOP)

1、概念:AOP是建立在Java的反射基礎之上,具體是指散落在程式中的公共部分提取出來,做成了切面類,這樣做的好處在於程式碼的可重用。一旦涉及到該功能的需求發生變化,只要修改該程式碼就行。AOP的實現主要是由JDK的動態代理與CGLIB代理。下面會具體介紹這兩種代理。

2、意義:增強類的功能(在目標物件的方法執行之間和執行之後)。

JDK動態代理:

a、先定義一個介面,這個介面中的方法是“目標方法”

package com.brickworkers;

public
interface Sky { public void rain(); }

b、接著寫一個這個介面的具體實現:

package com.brickworkers;

public class SkyImpl implements Sky{

    @Override
    public void rain() {
        System.out.println("it`s raining");
    }


}

c、如果要完成動態代理,首先需要定義一個InvocationHandler介面的子類:

package com.brickworkers;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyInvocationHandler implements InvocationHandler {

    //目標物件
    private Object obj = null;


    //獲取目標物件的代理物件
    public Object getProxy(Object obj){
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
    }

    //控制執行目標物件的方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("目標物件方法執行之前");
        Object result = method.invoke(obj, args);
        System.out.println("目標物件方法執行之後");
        return result;
    }

}

d:JDK動態代理測試類:

package com.brickworkers;

public class ProxyTest {

    public static void main(String[] args) {
        //例項化InvocationHandler
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        //生產代理物件
        Sky sky = (Sky) myInvocationHandler.getProxy(new SkyImpl());
        sky.rain();
    }

}

//執行結果:  目標物件方法執行之前
//            it`s raining
//          目標物件方法執行之後
看到這裡相信大家和我一樣就很疑惑,為什麼JDK動態代理只能侷限於介面呢?對此,筆者查閱了一些技術文件和JDK動態代理的原始碼,發現在反編譯產生的proxyTest.class中,類的定義如下:
import dynamic.proxy.UserService;  
import java.lang.reflect.*;  

public final class $ProxyTest extends Proxy  
    implements Sky
{ 
    ......
}

從反編譯的原始碼可以看出,proxyTest繼承了Proxy,然而在Java中只支援單繼承,但是可以實現多個介面,所以JDK動態代理只能侷限於介面。

那麼JDK實現動態代理需要實現類通過介面定義業務方法,對於沒有介面的類,要實現動態代理要怎麼辦呢?這個時候就需要CGLib動態代理啦。

CGLib動態代理:

a、定義一個目標物件:

package com.brickworkers;

public class Color {

    public void showColor(){
        System.out.println("red");
    }

}

b、如果要完成動態代理,首先需要定義一個MethodInterceptor介面的子類:

package com.brickworkers;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class MyCglib implements MethodInterceptor {

    //目標物件
    private Object obj = null;

    public Object getProxy(Object obj){
        this.obj = obj;
        Enhancer enhancer = new Enhancer();  
        enhancer.setSuperclass(obj.getClass());
        // 回撥方法  
        enhancer.setCallback(this);  
        // 建立代理物件  
        return enhancer.create();

    }

    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("目標物件方法執行之前");
        Object result = methodProxy.invoke(obj, args);
        System.out.println("目標方法執行之後");
        return result;
    }

}

c、CGLib動態代理測試類:

package com.brickworkers;

public class CGLibTest {

    public static void main(String[] args) {
        MyCglib myCglib = new MyCglib();
        Color color = (Color) myCglib.getProxy(new Color());
        color.showColor();
    }
}
//執行結果:目標物件方法執行之前
//              red
//          目標方法執行之後

最後,還有一點需要注意:因為CGLib動態代理是建立一個子類來實現的,那麼對於繼承的定義,final類是無法進行代理的哦。

相信能看到這裡的,都是和筆者一樣具有很大求知慾的人,望我們一起進步。