JAVA基础知识之JVM-——动态代理(AOP)
代理模式簡介
在很多場景下,我們想使用一個類(通常是接口)A時,并不是直接使用這個類,而是通過另外一個類B去調用A的方法,這里的類B就是一個代理類。
有很多場景都會用到這種方法,例如假如創(chuàng)建類A需要很大的開銷,我們會直接使用類B來代表類A。 又或者類A在遠程主機上,我們沒有權限直接調用A的方法,而代理類B卻有權限,我們可以調用B從而訪問到A。 又或者我們需要批量給A的方法加強一些功能,而我們沒有權限修改A,或者修改A會導致其他關聯(lián)的類因為耦合性需要修改,這時候我們也同樣可以通過代理類B來增加功能。 這種間接訪問A的方式就是一種非常常見的代理模式——通常我們只需要知道一個接口里提供了哪些方法,但并不需要方法細節(jié),我們可以通過代理類來控制這些方法的調用,并滿足例如前面提到的各種場景的功能要求。
動態(tài)代理
JAVA的代理模式大體上分為靜態(tài)代理(編譯期間就已經明確代理類及委托類的細節(jié))和動態(tài)代理(在JVM中動態(tài)生成一個代理類,通過動態(tài)生成的代理類去訪問委托類)。
動態(tài)代理也稱為AOP模式,即面向切面編程。 實現(xiàn)AOP的方式有很多,目前主要有兩種,一種是JDK自己實現(xiàn)的基于接口的動態(tài)代理,另一種是cglib方式,不需要強制實現(xiàn)接口。
本篇只簡單介紹JDK自帶的動態(tài)代理。
JDK動態(tài)代理
java動態(tài)代理主要涉及幾個角色,目標類(接口-target),代理對象(proxy),處理器類(接口-InvocationHandler)。
實現(xiàn)原理大致是這樣的,通過代理類proxy訪問目標類target時,在JDK底層,會動態(tài)生成一個proxy類(其實是子類),同時讓會讓這個proxy類實現(xiàn)target接口,這個Proxy類包含一個InvocationHandler類型的成員變量,后面就會用處理器對象去初始化這個Proxy類對象。proxy中對target類所有的方法實現(xiàn)都交給InvocationHandler的實現(xiàn)類去處理,通過在proxy中重寫target方法的實現(xiàn),而在實現(xiàn)中直接調用InvocationHandler的invoke方法,這樣就變成了我們調用代理proxy類執(zhí)行target方法的時候,實際上是執(zhí)行了InvocationHandler的invoke方法,而在invoke方法中,我們可以加入我們自己的邏輯,然后才調用真正的target中的方法。
整個過程可以用下面兩段偽代碼來模擬,
首先根據我們?yōu)閜roxy傳入的參數(target所有接口方法,InvocationHandler實例),JDK會在底層動態(tài)地為我們創(chuàng)建一個代理類,代理類如下,
1 class Proxy0 extends Proxy implements target {
2
3 private Method m1;
4
5 //用InvocationHandler去初始化proxy, 后期就可以將proxy的代理的方法交給InvocationHandler去處理
6 public Proxy0(InvocationHandler handler) {
7
8 supper(hander)
9 }
10
11 public void setM1(Method m1) {
12
13 this.m1 = m1;
14 }
15
16 private void method1(Object para) {
17
18 this.hander.invoke(this, m1, new object[] {para});
19 }
20
21 }
這段代理類的動態(tài)生成過程都由JVM控制,我們不可見(當然可以通過特殊方式拿到)。可以看到代理類會實現(xiàn)目標類target的所有方法,但是實現(xiàn)類中是調用了InvocationHandler的invoke方法的,這個invoke方法是需要我們自己去實現(xiàn)的,一般類似這樣,
1 class InvocationHandlerImpl implements InvocationHandler {
2 private Object target; 3 @Override
4 public Object invoke(Object proxy, Method method, Object[] args) {
5 // 加入我們自己的代碼
6 ....
7 //調用目標類的真正方法
8 method.invoke(target, args);
9 //加入我們自己的代碼
10 .....
11 }
關鍵點在于第2行和第8行,使用了反射執(zhí)行了目標類的真正需要被代理的方法。
以上整個過程,就是proxy代理的實現(xiàn)原理。
下面這張圖片借用了別人的博客
下面是一個簡單的例子,演示JDK動態(tài)代理的用法,
定義一個Dog接口, JDK的動態(tài)代理是基于接口的,其他動態(tài)代理可以基于繼承的方式。
1 package aop;
2
3 public interface Dog {
4 void info();
5 void run();
6 }
為接口寫個實現(xiàn)類,
1 package aop;
2
3 public class GunDog implements Dog {
4
5 @Override
6 public void info() {
7 // TODO Auto-generated method stub
8 System.out.println("我是一只獵狗");
9 }
10
11 @Override
12 public void run() {
13 // TODO Auto-generated method stub
14 System.out.println("我奔跑迅速");
15 }
16
17 }
寫個工具類,
1 package aop;
2
3 public class DogUtil {
4 public void method1() {
5 System.out.println("====this is method1====");
6 }
7
8 public void method2() {
9 System.out.println("====this is method2====");
10 }
11 }
關鍵點,寫一個處理器,實現(xiàn)InvocationHandler接口,
在invoke中加上我們自己的邏輯,再調用真正的target的方法。
后續(xù)代理類在處理target的所有方法時,都會在底層調用invoke方法,即將調用交給invoke了
1 package aop;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Method;
5
6 public class MyInvocationHandler implements InvocationHandler {
7
8 private Object target;
9
10 public void setTarget(Object target) {
11 this.target = target;
12 }
13
14 @Override
15 public Object invoke(Object proxy, Method method, Object[] args)
16 throws Throwable {
17 // TODO Auto-generated method stub
18 DogUtil du = new DogUtil();
19 du.method1();
20 Object result = method.invoke(target, args);
21 du.method2();
22 return result;
23 }
24
25 }
寫一個工廠類獲取proxy類實例,簡化編程
1 package aop;
2
3 import java.lang.reflect.Proxy;
4
5 public class MyProxyFactory {
6 public static Object getProxy(Object target) {
7 MyInvocationHandler handler = new MyInvocationHandler();
8 handler.setTarget(target);
9 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
10 }
11 }
下面是測試類,
1 package aop;
2
3 public class Test {
4 public static void main(String[] args) {
5 Dog target = new GunDog();
6 Dog dog = (Dog)MyProxyFactory.getProxy(target);
7 dog.info();
8 dog.run();
9 }
10 }
執(zhí)行結果如下,
1 代理開始。。。 2 我是一只獵狗 3 代理結束。。。 4 代理開始。。。 5 我奔跑迅速 6 代理結束。。。
reference
http://blog.csdn.net/luanlouis/article/details/24589193
總結
以上是生活随笔為你收集整理的JAVA基础知识之JVM-——动态代理(AOP)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构——图-有向图和无向图的邻接表基
- 下一篇: 教大家学习一下主板线路图中名词主板上的线