使用Java原生代理實現AOP

本文由博主柒。原創,轉載請註明出處

完整源碼下載地址 https://github.com/MatrixSeven/JavaAOP

一說到AOP,你們必定會想到Spring,由於這東西實在是太強大了.可是你們必定要清楚,AOP是一隻編程思想,而Spring僅僅是AOP的一種實現罷了.
java

首先百度下:

在軟件業,AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。

若是你對AOP尚未了解請左移百度百科:http://baike.baidu.com/search/word?word=AOP查看.

今天呢,我們就一塊兒用Java原生代理實現簡單的AOP功能.

首先,你得須要瞭解基本的反射知識,不然可能會感到困惑.

不羅嗦了,直接開始擼碼

首先,我們先寫一個簡單的接口.名字叫AnimalInterface,用來聲明規範動物的一些基本方法.

這些方法包括 設置名字,獲取名字,叫聲,屬性(原諒我沒文化,其實就是得到是陸棲仍是水棲或者水陸兩棲)
git

package proxy.imp;
public interface AnimalInterface {
    //設置名字
    void setName(String name);
    //獲取名字
    String getName();
    //叫聲
    void say();
    //獲取棲性
    void getProperty();
}

而後我們實現這個接口,建立一個名叫小黑的Doggithub

package proxy;
 
import proxy.imp.AnimalInterface;
public class DogImp implements AnimalInterface {
    private String name = "小黑";
    public DogImp() {
    }
    @Override
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String getName() {
        return this.name;
    }
    @Override
    public void say() {
        System.out.println("小狗:汪汪汪汪.....");
    }
    @Override
    public void getProperty() {
        System.out.println("小狗是陸地動物,可是會游泳哦");
    }
}

你們必定火燒眉毛了,怎麼實現相似AOP的功能呢….

我們先建立一個名爲AOPHandle的類,讓其實現InvocationHandler接口,

不能使用invoke時使用proxy做爲反射參數時,由於代理對象的接口,不一樣於對象,

這種代理機制是面向接口,而不是面向類的,若是使用proxy,會形成無限遞歸.而後就是棧溢出,可是依舊能反射成功一次,

這說明代理對象和對象的代理是不同的,可是我們能夠經過proxy參數的proxy.getClass()得到class對象,而後得到被代理

類的方法和參數,這也爲註解注入,特定方法注入,屬性注入提供了一種實現途徑吧,關於這個,我們後面再說..
編程

package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class AOPHandle implements InvocationHandler{
    //保存對象
    private Object o;
    public AOPHandle(Object o) {
        this.o=o;
    }
    /**
     * 這個方法會自動調用,Java動態代理機制
     * 會傳入下面是個參數
     * @param Object proxy  代理對象的接口,不一樣於對象
     * @param Method method 被調用方法
     * @param Object[] args 方法參數
     * 不能使用invoke時使用proxy做爲反射參數時,由於代理對象的接口,不一樣於對象
     * 這種代理機制是面向接口,而不是面向類的
     **/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法返回值
        Object ret=null;
        //打印方法名稱
        System.err.println("執行方法:"+method.getName()+"n參數類型爲:");
        //打印參數
        for(Class type:method.getParameterTypes())
            System.err.println(type.getName());
        //打印返回類型
        System.err.println("返回數據類型:"+method.getReturnType().getName());
        //反射調用方法
        ret=method.invoke(o, args);
        //聲明結束
        System.err.println("方法執行結束");
        //返回反射調用方法的返回值
        return ret;
    }
}

動態代理已經搞定..而後就是我們的AnimalFactory了..我們繼續
框架

package proxy;
import java.lang.reflect.Proxy;
public class AnimalFactory {
    /***
     * 獲取對象方法
     * @param obj
     * @return
     */
    private static Object getAnimalBase(Object obj){
        //獲取代理對象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(), new AOPHandle(obj));
    }
    /***
     * 獲取對象方法
     * @param obj
     * @return
     */
    @SuppressWarnings("unchecked")
    public static  T getAnimal(Object obj){
        return (T) getAnimalBase(obj);
    }
    /***
     * 獲取對象方法
     * @param className
     * @return
     */
    @SuppressWarnings("unchecked")
    public static   T getAnimal(String className){
        Object obj=null;
        try {
            obj= getAnimalBase(Class.forName(className).newInstance());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T)obj;
    }
    /***
     * 獲取對象方法
     * @param clz
     * @return
     */
    @SuppressWarnings("unchecked")
    public static   T  getAnimal(Class clz){
        Object obj=null;
        try {
            obj= getAnimalBase(clz.newInstance());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T)obj;
    }
}

終於到最後了…還差什麼呢,你們來這裏看看效果吧…

哈哈…小二,上個菜..哦~不對,是個測試類..哈哈////ide

package proxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import proxy.AnimalFactory;
import proxy.imp.AnimalInterface;
 
@RunWith(BlockJUnit4ClassRunner.class)
public class AOPTest {
 
    @Test
    public void Test1() {
        AnimalInterface dog=AnimalFactory.getAnimal(DogImp.class);
        dog.say();
        System.out.println("個人名字是"+dog.getName());
        dog.setName("二狗子");
        System.out.println("個人名字是"+dog.getName());
    }
}

對,我們的效果已經看到了..
,咦,你會說沒圖沒真相???
好,那就上圖…函數式編程

圖片在sae上已經丟失,請你們腦補

啥?什麼,,到了最後說,,這又卵用,這不是坑爹麼?就捕獲一個這個玩意,什麼用啊…
什麼AOP,我怎麼一點AOP的影子都沒有看到,怎麼切入自定義方法,就一個syso輸入,往這忽悠觀衆來了?…..
好吧,那我們繼續…看看如何實現注入自定義方法…

首先增長一個接口,我們就稱爲AOP注入接口吧.取名AOPMethod哈
建立after和before方法,接收Object proxy, Method method, Object[] args參數
這樣就能作更多的事情叻…好比執行方法前,記錄類狀態,寫入log.監控xx變量,,,
開啓你的腦洞吧.函數

package proxy.imp;
import java.lang.reflect.Method;
 
public interface AOPMethod{
    //實例方法執行前執行的方法
    void after(Object proxy, Method method, Object[] args);
    //實例方法執行後執行的方法
    void before(Object proxy, Method method, Object[] args);
}

而後修改AOPHandle類,增長AOPMethod屬性.

在修改構造方法,讓在類初始化時得到AOPMethod實例.

最後修改invoke方法….直接上代碼哈
學習

package proxy;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
import proxy.imp.AOPMethod;
 
public class AOPHandle implements InvocationHandler{
    //保存對象
    private AOPMethod method;
    private Object o;
    public AOPHandle(Object o,AOPMethod method) {
        this.o=o;
        this.method=method;
    }
    /**
     * 這個方法會自動調用,Java動態代理機制
     * 會傳入下面是個參數
     * @param Object proxy  代理對象的接口,不一樣於對象
     * @param Method method 被調用方法
     * @param Object[] args 方法參數
     * 不能使用invoke時使用proxy做爲反射參數時,由於代理對象的接口,不一樣於對象
     * 這種代理機制是面向接口,而不是面向類的
     **/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object ret=null;
        //修改的地方在這裏哦
        this.method.before(proxy, method, args);
        ret=method.invoke(o, args);
        //修改的地方在這裏哦
        this.method.after(proxy, method, args);
        return ret;
    }
}

呼呼,大功告成,,看起來一切都麼問題,萌萌噠..

趕忙更新下測試類…測試

package proxy;
 
import java.lang.reflect.Method;
 
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import proxy.imp.AOPMethod;
import proxy.imp.AnimalInterface;
 
@RunWith(BlockJUnit4ClassRunner.class)
public class AOPTest {
 
    public static void main(String[] args) {
 
        AnimalInterface dog = AnimalFactory.getAnimal(DogImp.class, new AOPMethod() {
            // 這裏寫方法執行前的AOP切入方法
            public void before(Object proxy, Method method, Object[] args) {
                System.err.println("我在" + method.getName() + "方法執行前執行");
            }
 
            // 這裏系方法執行後的AOP切入方法
            public void after(Object proxy, Method method, Object[] args) {
                System.err.println("我在 " + method.getName() + "方法執行後執行");
 
            }
        });
        dog.say();
        String name1="個人名字是" + dog.getName();
        System.out.println(name1);
        dog.setName("二狗子");
        String name2="個人名字是"+dog.getName();
        System.out.println(name2);
    }
}

來個效果圖:

圖片在sae上已經丟失,請你們腦補

呼呼,親們,是否是有注入的感受了?是否是感受把本身的方法切進去了???哈哈…. 看起來一切都已經完美了,可是總以爲差了點什麼?哦,對,缺乏了相似於Spring那麼樣的配置文件.. 其實那些已經很簡單了,交給大家去作吧,設計好XML格式化就妥了,等等,你說什麼,還不能攔截自定義方法? 不能像Spring那樣攔截自定義方法?oh~~NO,其實已經能夠了在 before(Object proxy, Method method, Object[] args) 中利用method和的給methodName就能作判斷了. 固然,本例的並無什麼實用意義,更不能個各類完善的AOP框架相比,本文僅僅爲您提供一種思路,可是必定要記住,再牛的東西也是一點點積累出來的 學習瞭解原理,是爲了更好的駕馭所掌握的知識和技能.咱再會回嘿嘿.

相關文章
相關標籤/搜索