詳解使用Java原生代理實(shí)現(xiàn)AOP實(shí)例
一說到AOP,大家一定會想到spring,因?yàn)檫@東西實(shí)在是太強(qiáng)大了.但是大家一定要清楚,AOP是一只編程思想,而Spring僅僅是AOP的一種實(shí)現(xiàn)罷了.
首先百度下:
在軟件業(yè),AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,通過預(yù)編譯方式和運(yùn)行期動態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。AOP是OOP的延續(xù),是軟件開發(fā)中的一個(gè)熱點(diǎn),也是Spring框架中的一個(gè)重要內(nèi)容,是函數(shù)式編程的一種衍生范型。利用AOP可以對業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開發(fā)的效率。
今天呢,咱們就一起用Java原生代理實(shí)現(xiàn)簡單的AOP功能.
首先,你得需要了解基本的反射知識,否則可能會感到困惑.
不羅嗦了,直接開始擼碼
首先,咱們先寫一個(gè)簡單的接口.名字叫AnimalInterface,用來聲明規(guī)范動物的一些基本方法.
這些方法包括 設(shè)置名字,獲取名字,叫聲,屬性(原諒我沒文化,其實(shí)就是獲得是陸棲還是水棲或者水陸兩棲)
package proxy.imp; public interface AnimalInterface { //設(shè)置名字 void setName(String name); //獲取名字 String getName(); //叫聲 void say(); //獲取棲性 void getProperty(); }
然后咱們實(shí)現(xiàn)這個(gè)接口,創(chuàng)建一個(gè)名叫小黑的Dog
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("小狗是陸地動物,但是會游泳哦"); } }
大家一定迫不及待了,怎么實(shí)現(xiàn)類似AOP的功能呢….
咱們先創(chuàng)建一個(gè)名為AOPHandle的類,讓其實(shí)現(xiàn)InvocationHandler接口,不能使用invoke時(shí)使用proxy作為反射參數(shù)時(shí),因?yàn)榇韺ο蟮慕涌?不同于對象,這種代理機(jī)制是面向接口,而不是面向類的,如果使用proxy,會造成無限遞歸.然后就是棧溢出,但是依舊能反射成功一次,這說明代理對象和對象的代理是不一樣的,但是咱們可以通過proxy參數(shù)的proxy.getClass()獲得class對象,然后獲得被代理類的方法和參數(shù),這也為注解注入,特定方法注入,屬性注入提供了一種實(shí)現(xiàn)途徑吧,關(guān)于這個(gè),咱們后面再說..
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; } /** * 這個(gè)方法會自動調(diào)用,Java動態(tài)代理機(jī)制 * 會傳入下面是個(gè)參數(shù) * @param Object proxy 代理對象的接口,不同于對象 * @param Method method 被調(diào)用方法 * @param Object[] args 方法參數(shù) * 不能使用invoke時(shí)使用proxy作為反射參數(shù)時(shí),因?yàn)榇韺ο蟮慕涌?不同于對象 * 這種代理機(jī)制是面向接口,而不是面向類的 **/ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法返回值 Object ret=null; //打印方法名稱 System.err.println("執(zhí)行方法:"+method.getName()+"n參數(shù)類型為:"); //打印參數(shù) for(Class type:method.getParameterTypes()) System.err.println(type.getName()); //打印返回類型 System.err.println("返回?cái)?shù)據(jù)類型:"+method.getReturnType().getName()); //反射調(diào)用方法 ret=method.invoke(o, args); //聲明結(jié)束 System.err.println("方法執(zhí)行結(jié)束"); //返回反射調(diào)用方法的返回值 return ret; } }
動態(tài)代理已經(jīng)搞定..然后就是咱們的AnimalFactory了..咱們繼續(xù)
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; } }
終于到最后了…還差什么呢,大家來這里看看效果吧…
哈哈…小二,上個(gè)菜..哦~不對,是個(gè)測試類..哈哈////
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()); } }
啥?什么,,到了最后說,,這又卵用,這不是坑爹么?就捕獲一個(gè)這個(gè)玩意,什么用啊…
什么AOP,我怎么一點(diǎn)AOP的影子都沒有看到,怎么切入自定義方法,就一個(gè)syso輸入,往這忽悠觀眾來了?…..
好吧,那咱們繼續(xù)…看看如何實(shí)現(xiàn)注入自定義方法…
首先增加一個(gè)接口,咱們就稱為AOP注入接口吧.取名AOPMethod哈
創(chuàng)建after和before方法,接收Object proxy, Method method, Object[] args參數(shù)
這樣就能做更多的事情叻…比如執(zhí)行方法前,記錄類狀態(tài),寫入log.監(jiān)控xx變量,,,
開啟你的腦洞吧.
package proxy.imp; import java.lang.reflect.Method; public interface AOPMethod{ //實(shí)例方法執(zhí)行前執(zhí)行的方法 void after(Object proxy, Method method, Object[] args); //實(shí)例方法執(zhí)行后執(zhí)行的方法 void before(Object proxy, Method method, Object[] args); }
然后修改AOPHandle類,增加AOPMethod屬性.
在修改構(gòu)造方法,讓在類初始化時(shí)獲得AOPMethod實(shí)例.
最后修改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; } /** * 這個(gè)方法會自動調(diào)用,Java動態(tài)代理機(jī)制 * 會傳入下面是個(gè)參數(shù) * @param Object proxy 代理對象的接口,不同于對象 * @param Method method 被調(diào)用方法 * @param Object[] args 方法參數(shù) * 不能使用invoke時(shí)使用proxy作為反射參數(shù)時(shí),因?yàn)榇韺ο蟮慕涌?不同于對象 * 這種代理機(jī)制是面向接口,而不是面向類的 **/ @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() { // 這里寫方法執(zhí)行前的AOP切入方法 public void before(Object proxy, Method method, Object[] args) { System.err.println("我在" + method.getName() + "方法執(zhí)行前執(zhí)行"); } // 這里系方法執(zhí)行后的AOP切入方法 public void after(Object proxy, Method method, Object[] args) { System.err.println("我在 " + method.getName() + "方法執(zhí)行后執(zhí)行"); } }); dog.say(); String name1="我的名字是" + dog.getName(); System.out.println(name1); dog.setName("二狗子"); String name2="我的名字是"+dog.getName(); System.out.println(name2); } }
呼呼,親們,是不是有注入的感覺了?是不是感覺把自己的方法切進(jìn)去了???哈哈….
看起來一切都已經(jīng)完美了,但是總覺得差了點(diǎn)什么?哦,對,缺少了類似于Spring那么樣的配置文件..
其實(shí)那些已經(jīng)很簡單了,交給你們?nèi)プ霭?設(shè)計(jì)好XML格式化就妥了,等等,你說什么,還不能攔截自定義方法?
不能像Spring那樣攔截自定義方法?oh~~NO,其實(shí)已經(jīng)可以了在before(Object proxy, Method method, Object[] args)中利用method和的給methodName就能做判斷了.
當(dāng)然,本例的并沒有什么實(shí)用意義,更不能個(gè)各種完善的AOP框架相比,本文僅僅為您提供一種思路,但是一定要記住,再牛的東西也是一點(diǎn)點(diǎn)積累出來的
實(shí)例下載:http://xiazai.jb51.net/201701/yuanma/JavaAOP_jb51.rar
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- java基于spring注解AOP的異常處理的方法
- Java之Spring AOP 實(shí)現(xiàn)用戶權(quán)限驗(yàn)證
- Java AOP知識詳細(xì)介紹
- Java實(shí)現(xiàn)AOP面向切面編程的實(shí)例教程
- 實(shí)例講解Java的Spring框架中的AOP實(shí)現(xiàn)
- Java的Spring框架中AOP項(xiàng)目的一般配置和部署教程
- 舉例講解Java的Spring框架中AOP程序設(shè)計(jì)方式的使用
- Java的Spring框架下的AOP編程模式示例
- Java動態(tài)代理實(shí)現(xiàn)AOP
- 詳解Java反射實(shí)現(xiàn)Aop代理
相關(guān)文章
基于Java回顧之網(wǎng)絡(luò)通信的應(yīng)用分析
在這篇文章里,我們主要討論如何使用Java實(shí)現(xiàn)網(wǎng)絡(luò)通信,包括TCP通信、UDP通信、多播以及NIO2013-05-05Java中如何將list轉(zhuǎn)為樹形結(jié)構(gòu)
這篇文章主要介紹了Java中如何將list轉(zhuǎn)為樹形結(jié)構(gòu),本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09Java 判斷字符串a(chǎn)和b是否互為旋轉(zhuǎn)詞
本篇文章主要介紹了判斷字符串a(chǎn)和b是否互為旋轉(zhuǎn)詞的相關(guān)知識,具有很好的參考價(jià)值。下面跟著小編一起來看下吧2017-05-05Java動態(tài)代理靜態(tài)代理實(shí)例分析
這篇文章主要介紹了Java動態(tài)代理靜態(tài)代理實(shí)例分析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03SpringCloud添加客戶端Eureka Client過程解析
這篇文章主要介紹了SpringCloud添加客戶端Eureka Client過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03