1、代理的概念與做用java
生活中的代理
武漢人從武漢的代理商手中買聯想電腦和直接跑到北京傳智播客旁邊來找聯想總部買電腦,你以爲最終的主體業務目標有什麼區別嗎?基本上同樣吧,都解決了核心問題,可是,一點區別都沒有嗎?從代理商那裏買真的一點好處都沒有嗎?
程序中的代理
要爲已存在的多個具備相同接口的目標類的各個方法增長一些系統功能,例如,異常處理、日誌、計算方法的運行時間、事務管理、等等,你準備如何作?
編寫一個與目標類具備相同接口的代理類,代理類的每一個方法調用目標類的相同方法,並在調用方法時加上系統功能的代碼。 (參看下頁的原理圖)
若是採用工廠模式和配置文件的方式進行管理,則不須要修改客戶端程序,在配置文件中配置是使用目標類、仍是代理類,這樣之後很容易切換,譬如,想要日誌功能時就配置代理類,不然配置目標類,這樣,增長系統功能很容易,之後運行一段時間後,又想去掉系統功能也很容易。編程
2、代理架構圖安全
3、AOP架構
系統中存在交叉業務,一個交叉業務就是要切入到系統中的一個方面,以下所示:
安全 事務 日誌
StudentService ------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService ------|----------|------------|-------------
用具體的程序代碼描述交叉業務:
method1 method2 method3
{ { {
------------------------------------------------------切面
.... .... ......
------------------------------------------------------切面
} } }
交叉業務的編程問題即爲面向方面的編程(Aspect oriented program ,簡稱AOP),AOP的目標就是要使交叉業務模塊化。能夠採用將切面代碼移動到原始方法的周圍,這與直接在方法中編寫切面代碼的運行效果是同樣的,以下所示:
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
使用代理技術正好能夠解決這種問題,代理是實現AOP功能的核心和關鍵技術。
4、動態代理技術app
要爲系統中的各類接口的類增長代理功能,那將須要太多的代理類,所有采用靜態代理方式,將是一件很是麻煩的事情!寫成百上千個代理類,是否是太累!
JVM能夠在運行期動態生成出類的字節碼,這種動態生成的類每每被用做代理類,即動態代理類。
JVM生成的動態類必須實現一個或多個接口,因此,JVM生成的動態類只能用做具備相同接口的目標類的代理。
CGLIB庫能夠動態生成一個類的子類,一個類的子類也能夠用做該類的代理,因此,若是要爲一個沒有實現接口的類生成動態代理類,那麼可使用CGLIB庫。
代理類的各個方法中一般除了要調用目標的相應方法和對外返回目標返回的結果外,還能夠在代理方法中的以下四個位置加上系統功能代碼:
1.在調用目標方法以前
2.在調用目標方法以後
3.在調用目標方法先後
4.在處理目標方法異常的catch塊中
5、分析JVM動態生成的類模塊化
5.一、建立實現了Collection接口的動態類和查看其名稱,分析Proxy.getProxyClass方法的各個參數。函數
Class clazz = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); System.out.println(clazz.getName());//結果爲:$Proxy0
5.二、編碼列出動態類中的全部構造方法和參數簽名
ui
Constructor[] constructors = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class).getConstructors(); for(Constructor constructor:constructors){ System.out.println(constructor); StringBuilder paramString = new StringBuilder(); paramString.append("("); Class[] params = constructor.getParameterTypes(); for(Class param :params){ paramString.append(param.getName()).append(","); } if(params !=null || params.length != 0) paramString.deleteCharAt(paramString.length()-1); paramString.append(")"); System.out.println(paramString); } 結果:public $Proxy0(java.lang.reflect.InvocationHandler) (java.lang.reflect.InvocationHandler)
5.三、編碼列出動態類中的全部方法和參數簽名編碼
Class clazz = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); Method[] methods = clazz.getMethods(); for(Method method : methods){ System.out.println(method.getName()); for(Class param : method.getParameterTypes()){ System.out.println(param.getName()+"\r\n"); } }
5.四、建立動態類的實例對象spa
獲得一個代理類:
Constructor constructor = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class).getConstructor(InvocationHandler.class); class MyInvocationHandler implements InvocationHandler{ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } } Collection proxyCollection = (Collection)constructor.newInstance(new MyInvocationHandler());
方法1、Proxy.getProxyClass經過獲得代理類的字節碼,而後獲得構造函數,而後建立實列
Constructor constructor = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class).getConstructor(InvocationHandler.class); final ArrayList targe = new ArrayList(); class MyInvocationHandler implements InvocationHandler{ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(targe, args); } } Collection proxyCollection = (Collection)constructor.newInstance(new MyInvocationHandler()); proxyCollection.add("ccc"); System.out.println(proxyCollection.size()); 結果:1
方法2、用Proxy.newInstance方法直接一步就建立出代理對象。
Collection proxyCollection = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(), new Class[]{Collection.class}, new InvocationHandler(){ ArrayList targe = new ArrayList(); public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long begintime = System.currentTimeMillis(); Object retVal =method.invoke(targe, args); long endtime = System.currentTimeMillis(); System.out.println(method.getName()+"方法調用的時間爲:"+(endtime-begintime)); return retVal; } } ); proxyCollection.add("xxx"); proxyCollection.size(); 結果爲:不一樣電腦結果不同,同一電腦每次運行的結果也不同 add方法調用的時間爲:1 size方法調用的時間爲:0
5.五、編寫一個簡單的動態代理方法
public static Object getProxy(final ArrayList targe,final Advice advice) { Object proxy = (Object)Proxy.newProxyInstance(Collection.class.getClassLoader(), new Class[]{Object.class}, new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /*long begintime = System.currentTimeMillis(); Object retVal =method.invoke(targe, args); long endtime = System.currentTimeMillis();*/ advice.beforeMethod(method); Object retVal =method.invoke(targe, args); advice.afterMethod(method); //System.out.println(method.getName()+"方法調用的時間爲:"+(endtime-begintime)); return retVal; } } ); return proxy; }
6、動態代理的原理圖