亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

轉:反射實現 AOP 動態代理模式(Spring AOP的實

系統 1767 0

好長時間沒有用過Spring了. 突然拿起書.我都發現自己對AOP都不熟悉了.
其實AOP的意思就是面向切面編程.
OO注重的是我們解決問題的方法(封裝成Method),而AOP注重的是許多解決解決問題的方法中的共同點,是對OO思想的一種補充!
還是拿人家經常舉的一個例子講解一下吧:
比如說,我們現在要開發的一個應用里面有很多的業務方法,但是,我們現在要對這個方法的執行做全面監控,或部分監控.也許我們就會在要一些方法前去加上一條日志記錄,
我們寫個例子看看我們最簡單的解決方案
我們先寫一個接口IHello.java代碼如下:

?1 package ?sinosoft.dj.aop.staticaop;
?2
?3 public ? interface ?IHello? {
?4 ???? /**
?5 ?????*?假設這是一個業務方法
?6 ?????*? @param ?name
?7 ????? */

?8 ???? void ?sayHello(String?name);
?9 }

10


里面有個方法,用于輸入"Hello" 加傳進來的姓名;我們去寫個類實現IHello接口

package ?sinosoft.dj.aop.staticaop;

public ? class ?Hello? implements ?IHello? {

???? public ? void ?sayHello(String?name)? {
????????System.out.println("Hello?"?+?name);
????}


}


現在我們要為這個業務方法加上日志記錄的業務,我們在不改變原代碼的情況下,我們會去怎么做呢?也許,你會去寫一個類去實現IHello接口,并依賴Hello這個類.代碼如下:

?1 package ?sinosoft.dj.aop.staticaop;
?2
?3 public ? class ?HelloProxy? implements ?IHello? {
?4 ???? private ?IHello?hello;
?5
?6 ???? public ?HelloProxy(IHello?hello)? {
?7 ???????? this .hello?=?hello;
?8 ????}

?9
10 ???? public ? void ?sayHello(String?name)? {
11 ????????Logger.logging(Level.DEBUGE,?"sayHello?method?start .");
12 ????????hello.sayHello(name);
13 ????????Logger.logging(Level.INFO,?"sayHello?method?end!");
14
15 ????}

16
17 }

18


其中.Logger類和Level枚舉代碼如下:
Logger.java

?1 package ?sinosoft.dj.aop.staticaop;
?2
?3 import ?java.util.Date;
?4
?5 public ? class ?Logger {
?6 ???? /**
?7 ?????*?根據等級記錄日志
?8 ?????*? @param ?level
?9 ?????*? @param ?context
10 ????? */

11 ???? public ? static ? void ?logging(Level?level,?String?context)? {
12 ???????? if ?(level.equals(Level.INFO))? {
13 ????????????System.out.println( new ?Date().toLocaleString()?+?"?"?+?context);
14 ????????}

15 ???????? if ?(level.equals(Level.DEBUGE))? {
16 ????????????System.err.println( new ?Date()?+?"?"?+?context);
17 ????????}

18 ????}

19
20 }

21

Level.java

1 package ?sinosoft.dj.aop.staticaop;
2
3 public ? enum ?Level? {
4 ????INFO,DEBUGE;
5 }

6

那我們去寫個測試類看看,代碼如下:
Test.java

1 package ?sinosoft.dj.aop.staticaop;
2
3 public ? class ?Test? {
4 ???? public ? static ? void ?main(String[]?args)? {
5 ????????IHello?hello?=? new ?HelloProxy( new ?Hello());
6 ????????hello.sayHello("Doublej");
7 ????}

8 }

9

運行以上代碼我們可以得到下面結果:

Tue?Mar?04?20:57:12?CST?2008?sayHello?method?start .
Hello?Doublej
2008-3-4?20:57:12?sayHello?method?end!


從上面的代碼我們可以看出,hello對象是被HelloProxy這個所謂的代理態所創建的.這樣,如果我們以后要把日志記錄的功能去掉.那我們只要把得到hello對象的代碼改成以下:

1 package ?sinosoft.dj.aop.staticaop;
2
3 public ? class ?Test? {
4 ???? public ? static ? void ?main(String[]?args)? {
5 ????????IHello?hello?=? new ?Hello();
6 ????????hello.sayHello("Doublej");
7 ????}

8 }

9


上面代碼,可以說是AOP最簡單的實現!
但是我們會發現一個問題,如果我們像Hello這樣的類很多,那么,我們是不是要去寫很多個HelloProxy這樣的類呢.沒錯,是的.其實也是一種很麻煩的事.在jdk1.3以后.jdk跟我們提供了一個API?? java.lang.reflect.InvocationHandler的類. 這個類可以讓我們在JVM調用某個類的方法時動態的為些方法做些什么事.讓我們把以上的代碼改一下來看看效果.
同樣,我們寫一個IHello的接口和一個Hello的實現類.在接口中.我們定義兩個方法;代碼如下 :

IHello.java

?1 package ?sinosoft.dj.aop.proxyaop;
?2
?3 public ? interface ?IHello? {
?4 ???? /**
?5 ?????*?業務處理A方法
?6 ?????*? @param ?name
?7 ????? */

?8 ???? void ?sayHello(String?name);
?9 ???? /**
10 ?????*?業務處理B方法
11 ?????*? @param ?name
12 ????? */

13 ???? void ?sayGoogBye(String?name);
14 }

15



Hello.java?

?1 package ?sinosoft.dj.aop.proxyaop;
?2
?3 public ? class ?Hello? implements ?IHello? {
?4
?5 ???? public ? void ?sayHello(String?name)? {
?6 ????????System.out.println("Hello?"?+?name);
?7 ????}

?8 ???? public ? void ?sayGoogBye(String?name)? {
?9 ????????System.out.println(name+"?GoodBye!");
10 ????}

11 }

12


我們一樣的去寫一個代理類.只不過.讓這個類去實現java.lang.reflect.InvocationHandler接口,代碼如下:

?1 package ?sinosoft.dj.aop.proxyaop;
?2
?3 import ?java.lang.reflect.InvocationHandler;
?4 import ?java.lang.reflect.Method;
?5 import ?java.lang.reflect.Proxy;
?6
?7 public ? class ?DynaProxyHello? implements ?InvocationHandler? {
?8
?9 ???? /**
10 ?????*?要處理的對象(也就是我們要在方法的前后加上業務邏輯的對象,如例子中的Hello)
11 ????? */

12 ???? private ?Object?delegate;
13
14 ???? /**
15 ?????*?動態生成方法被處理過后的對象?(寫法固定)
16 ?????*?
17 ?????*? @param ?delegate
18 ?????*? @param ?proxy
19 ?????*? @return
20 ????? */

21 ???? public ?Object?bind(Object?delegate)? {
22 ???????? this .delegate?=?delegate;
23 ???????? return ?Proxy.newProxyInstance(
24 ???????????????? this .delegate.getClass().getClassLoader(),? this .delegate
25 ????????????????????????.getClass().getInterfaces(),? this );
26 ????}

27 ???? /**
28 ?????*?要處理的對象中的每個方法會被此方法送去JVM調用,也就是說,要處理的對象的方法只能通過此方法調用
29 ?????*?此方法是動態的,不是手動調用的
30 ????? */

31 ???? public ?Object?invoke(Object?proxy,?Method?method,?Object[]?args)
32 ???????????? throws ?Throwable? {
33 ????????Object?result?=? null ;
34 ???????? try ? {
35 ???????????? // 執行原來的方法之前記錄日志
36 ????????????Logger.logging(Level.DEBUGE,?method.getName()?+?"?Method?end? .");
37 ????????????
38 ???????????? // JVM通過這條語句執行原來的方法(反射機制)
39 ????????????result?=?method.invoke( this .delegate,?args);
40 ???????????? // 執行原來的方法之后記錄日志
41 ????????????Logger.logging(Level.INFO,?method.getName()?+?"?Method?Start!");
42 ????????}
? catch ?(Exception?e)? {
43 ????????????e.printStackTrace();
44 ????????}

45 ???????? // 返回方法返回值給調用者
46 ???????? return ?result;
47 ????}

48
49 }

50


上面類中出現的Logger類和Level枚舉還是和上一上例子的實現是一樣的.這里就不貼出代碼了.

讓我們寫一個Test類去測試一下.代碼如下:
Test.java

?1 package ?sinosoft.dj.aop.proxyaop;
?2
?3 public ? class ?Test? {
?4 ???? public ? static ? void ?main(String[]?args)? {
?5 ????????IHello?hello?=?(IHello) new ?DynaProxyHello().bind( new ?Hello());
?6 ????????hello.sayGoogBye("Double?J");
?7 ????????hello.sayHello("Double?J");
?8 ????????
?9 ????}

10 }

11


運行輸出的結果如下:

Tue?Mar?04?21:24:03?CST?2008?sayGoogBye?Method?end? .
Double?J?GoodBye!
2008-3-4?21:24:03?sayGoogBye?Method?Start!
Tue?Mar?04?21:24:03?CST?2008?sayHello?Method?end? .
Hello?Double?J
2008-3-4?21:24:03?sayHello?Method?Start!


由于線程的關系,第二個方法的開始出現在第一個方法的結束之前.這不是我們所關注的!
從上面的例子我們看出.只要你是采用面向接口編程,那么,你的任何對象的方法執行之前要加上記錄日志的操作都是可以的.他(DynaPoxyHello)自動去代理執行被代理對象(Hello)中的每一個方法,一個java.lang.reflect.InvocationHandler接口就把我們的代理對象和被代理對象解藕了.但是,我們又發現還有一個問題,這個DynaPoxyHello對象只能跟我們去在方法前后加上日志記錄的操作.我們能不能把DynaPoxyHello對象和日志操作對象(Logger)解藕呢?
結果是肯定的.讓我們來分析一下我們的需求.
我們要在被代理對象的方法前面或者后面去加上日志操作代碼(或者是其它操作的代碼),
那么,我們可以抽象出一個接口,這個接口里就只有兩個方法,一個是在被代理對象要執行方法之前執行的方法,我們取名為start,第二個方法就是在被代理對象執行方法之后執行的方法,我們取名為end .接口定義如下 :

?1 package ?sinosoft.dj.aop.proxyaop;
?2
?3 import ?java.lang.reflect.Method;
?4
?5 public ? interface ?IOperation? {
?6 ???? /**
?7 ?????*?方法執行之前的操作
?8 ?????*? @param ?method
?9 ????? */

10 ???? void ?start(Method?method);
11 ???? /**
12 ?????*?方法執行之后的操作
13 ?????*? @param ?method
14 ????? */

15 ???? void ?end(Method?method);
16 }

17


我們去寫一個實現上面接口的類.我們把作他真正的操作者,如下面是日志操作者的一個類:
LoggerOperation.java

package ?sinosoft.dj.aop.proxyaop;

import ?java.lang.reflect.Method;

public ? class ?LoggerOperation? implements ?IOperation? {

???? public ? void ?end(Method?method)? {
????????Logger.logging(Level.DEBUGE,?method.getName()?+?"?Method?end? .");
????}


???? public ? void ?start(Method?method)? {
????????Logger.logging(Level.INFO,?method.getName()?+?"?Method?Start!");
????}


}


然后我們要改一下代理對象DynaProxyHello中的代碼.如下:

?1 package ?sinosoft.dj.aop.proxyaop;
?2
?3 import ?java.lang.reflect.InvocationHandler;
?4 import ?java.lang.reflect.Method;
?5 import ?java.lang.reflect.Proxy;
?6
?7 public ? class ?DynaProxyHello? implements ?InvocationHandler? {
?8 ???? /**
?9 ?????*?操作者
10 ????? */

11 ???? private ?Object?proxy;
12 ???? /**
13 ?????*?要處理的對象(也就是我們要在方法的前后加上業務邏輯的對象,如例子中的Hello)
14 ????? */

15 ???? private ?Object?delegate;
16
17 ???? /**
18 ?????*?動態生成方法被處理過后的對象?(寫法固定)
19 ?????*?
20 ?????*? @param ?delegate
21 ?????*? @param ?proxy
22 ?????*? @return
23 ????? */

24 ???? public ?Object?bind(Object?delegate,Object?proxy)? {
25 ????????
26 ???????? this .proxy?=?proxy;
27 ???????? this .delegate?=?delegate;
28 ???????? return ?Proxy.newProxyInstance(
29 ???????????????? this .delegate.getClass().getClassLoader(),? this .delegate
30 ????????????????????????.getClass().getInterfaces(),? this );
31 ????}

32 ???? /**
33 ?????*?要處理的對象中的每個方法會被此方法送去JVM調用,也就是說,要處理的對象的方法只能通過此方法調用
34 ?????*?此方法是動態的,不是手動調用的
35 ????? */

36 ???? public ?Object?invoke(Object?proxy,?Method?method,?Object[]?args)
37 ???????????? throws ?Throwable? {
38 ????????Object?result?=? null ;
39 ???????? try ? {
40 ???????????? // 反射得到操作者的實例
41 ????????????Class?clazz?=? this .proxy.getClass();
42 ???????????? // 反射得到操作者的Start方法
43 ????????????Method?start?=?clazz.getDeclaredMethod("start",
44 ???????????????????? new ?Class[]? {?Method. class ?} );
45 ???????????? // 反射執行start方法
46 ????????????start.invoke( this .proxy,? new ?Object[]? {?method?} );
47 ???????????? // 執行要處理對象的原本方法
48 ????????????result?=?method.invoke( this .delegate,?args);
49 // ????????????反射得到操作者的end方法
50 ????????????Method?end?=?clazz.getDeclaredMethod("end",
51 ???????????????????? new ?Class[]? {?Method. class ?} );
52 // ????????????反射執行end方法
53 ????????????end.invoke( this .proxy,? new ?Object[]? {?method?} );
54
55 ????????}
? catch ?(Exception?e)? {
56 ????????????e.printStackTrace();
57 ????????}

58 ???????? return ?result;
59 ????}

60
61 }

62


然后我們把Test.java中的代碼改一下.測試一下:

package ?sinosoft.dj.aop.proxyaop;

public ? class ?Test? {
???? public ? static ? void ?main(String[]?args)? {
????????IHello?hello?=?(IHello) new ?DynaProxyHello().bind( new ?Hello(), new ?LoggerOperation());
????????hello.sayGoogBye("Double?J");
????????hello.sayHello("Double?J");
????????
????}

}

結果還是一樣的吧.

如果你想在每個方法之前加上日志記錄,而不在方法后加上日志記錄.你就把LoggerOperation類改成如下:

?1 package ?sinosoft.dj.aop.proxyaop;
?2
?3 import ?java.lang.reflect.Method;
?4
?5 public ? class ?LoggerOperation? implements ?IOperation? {
?6
?7 ???? public ? void ?end(Method?method)? {
?8 ???????? // Logger.logging(Level.DEBUGE,?method.getName()?+?"?Method?end? .");
?9 ????}

10
11 ???? public ? void ?start(Method?method)? {
12 ????????Logger.logging(Level.INFO,?method.getName()?+?"?Method?Start!");
13 ????}

14
15 }

16


運行一下.你就會發現,每個方法之后沒有記錄日志了. 這樣,我們就把代理者和操作者解藕了!

下面留一個問題給大家,如果我們不想讓所有方法都被日志記錄,我們應該怎么去解藕呢.?
我的想法是在代理對象的public Object invoke(Object proxy, Method method, Object[] args)方法里面加上個if(),對傳進來的method的名字進行判斷,判斷的條件存在XML里面.這樣我們就可以配置文件時行解藕了.如果有興趣的朋友可以把操作者,被代理者,都通過配置文件進行配置 ,那么就可以寫一個簡單的SpringAOP框架了.

?

?

轉: http://www.blogjava.net/DoubleJ/archive/2008/03/04/183796.html

?

更多: Java編程中使用動態代理實現AOP功能? ? http://developer.51cto.com/art/200906/130539.htm ?

轉:反射實現 AOP 動態代理模式(Spring AOP的實現原理)


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 爱搞逼综合网 | 国产91在线精品福利 | 狠狠色综合久久婷婷 | 久久精品视频一区 | 欧美精品亚洲一区二区在线播放 | 亚洲婷婷综合中文字幕第一页 | 亚洲综合激情六月婷婷在线观看 | 国产一级aaa全黄毛片 | 久久乱69小说 | 在线观看 亚洲 | 天天插天天操天天干 | 性做久久久久久久久25的美女 | 美国免费三片在线观看 | 在线播放色 | 一级毛片黄色片 | 欧美成免费 | 精品国产视频 | 精品一区二区三区免费爱 | 久久99精品久久久久久黑人 | 欧洲国产伦久久久久久久 | 日韩欧美国产精品第一页不卡 | 亚洲视频手机在线观看 | 91高清免费国产自产拍2021 | 婷婷久 | 国产偷久久 | 亚洲精品区在线播放一区二区 | 六色视频 | 热99精品 | 欧美成人一区二区三区不卡 | 日韩中文字幕在线观看视频 | 老师在办公室被躁到白浆 | 色偷偷尼玛图亚洲综合 | 特级黄色毛片视频 | 牛牛影视成人午夜影视 | 欧美视频在线观看免费 | 松永纱奈在线观看 | 看黄色一级毛片 | 香蕉精品视频在线观看入口 | 欧美男女爱爱视频 | 2021国产精品自产拍在线 | 久久一二|