黑馬程序員-java基礎加強-代理

-------------------------ASP.Net+Unity開發、.Net培訓、期待與您交流!--------------------------

代理

 代理類的生成是通過反射來進行的,同時代理對象的操作也是通過反射。
   1、總結
  要爲已經存在的目標類的各個方法,增加一些新的功能,如:異常處理、日誌、計算方法的
  運行時間、事務管理、等等,這些方法本身不能動,那麼可以通過代理來實現功能的增加,
  即,寫一個代理類,與此目標類實現相同的接口(爲了具有 相同的方法),然後再方法裏
  增加新功能,並調用目標類中的相同的方法,這樣代理類中的這個方法就是增加了新功能的方法。
  例如:
  class X
  {
  void sayHello()
  {
  System.out.println("hello world");
  }
  }
  爲了給此類中的方法sayHello增加一些功能,則利用代理,以後就不要再調用目標類的此方法了,
  而是直接使用代理類中的此方法。如下
  class XProxy
  {
  void sayHello()
  {
  //增加新的功能
starttime
X.sayHello();//調用目標類的相同的方法。
endtime
  }
  }
 即爲:
第一、要爲已存在的多個具有相同接口的目標類的各個方法增加一些系統功能,例如,異常處理、日誌、
計算方法的運行時間、事務管理、等等,你準備如何做?
第二、編寫一個與目標類具有相同接口的代理類,代理類的每個方法調用目標類的相同方法,並在調用
方法時加上系統功能的代碼。 (參看下頁的原理圖)
第三、如果採用工廠模式和配置文件的方式進行管理,則不需要修改客戶端程序,在配置文件中配置是
使用目標類、還是代理類,這樣以後很容易切換,譬如,想要日誌功能時就配置代理類,否則配置目
標類,這樣,增加系統功能很容易,以後運行一段時間後,又想去掉系統功能也很容易
------------------------------------------------------------------------------------------- 
AOP
 1、用具體的程序代碼描述交叉業務:
method1         method2          method3
{                      {                       { 
------------------------------------------------------切面
....            ....              ......
------------------------------------------------------切面
}                       }                       }
交叉業務的編程問題即爲面向方面的編程(Aspect oriented program ,簡稱AOP),AOP的目標就是要
使交叉業務模塊化。可以採用將切面代碼移動到原始方法的周圍,這與直接在方法中編寫切面代碼的
運行效果是一樣的,如下所示:
------------------------------------------------------切面
func1         func2            func3
{             {                { 
....            ....              ......
}             }                }
------------------------------------------------------切面
使用代理技術正好可以解決這種問題,代理是實現AOP功能的核心和關鍵技術。
2、代理類的生成原理
第一:當類實現了接口
JVM可以在運行期動態生成出類的字節碼,這種動態生成的類往往被用作代理類,即動態代理類。
JVM生成的動態類必須實現一個或多個接口,所以,JVM生成的動態類只能用作具有相同接口的目標類的代理。
    第二:類沒有實現任何接口
CGLIB庫可以動態生成一個類的子類,一個類的子類也可以用作該類的代理,所以,如果要爲一個沒有實現接
口的類生成動態代理類,那麼可以使用CGLIB庫。
代理類的各個方法中通常除了要調用目標的相應方法和對外返回目標返回的結果外,還可以在代理方法中的如下
四個位置加上系統功能代碼:
  1.在調用目標方法之前
  2.在調用目標方法之後
  3.在調用目標方法前後
  4.在處理目標方法異常的catch塊中
-------------------------------------------------------------------------------------------
創建動態類及查看其方法列表信息

程序:

package cn.itcast.day3;
		import java.lang.reflect.Constructor;
		import java.lang.reflect.Method;
		import java.lang.reflect.Proxy;
		import java.util.Collection;
		public class ProxyTest {
			public static void main(String[] args) {
			// TODO Auto-generated method stub
			System.out.println("----------第一步:生產動態類----------------");
			//調用生成動態類Proxy.getProxyClass(類加載器,實現的接口),
			//通常類加載器是與實現的接口的類加載器是一樣的。
			Class clazzProxy=Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
			System.out.println(clazzProxy.getName());
			System.out.println("----------第二步:查看此類中的構造方法----------------");
			//得到構造方法
			Constructor[] constructors=clazzProxy.getConstructors();
			//循環得到所有的構造方法
			//打印的構造函數形式爲:構造函數名(參數類型,參數類型)
			//即$Proxy0()和$Proxy0(int,String)這種
			for (Constructor constructor : constructors) {
				String sc=constructor.getName();//com.sun.proxy.$Proxy0
				String sc1=sc.substring(sc.lastIndexOf('.')+1);//$Proxy0
				StringBuilder sb=new StringBuilder();
				//將獲得的構造函數名加入到容器中。
				sb.append(sc1);
				sb.append('(');
				//獲取此構造函數中的參數類型
				Class[] clazzParams=constructor.getParameterTypes();
				if (clazzParams!=null&&clazzParams.length!=0) {
					for (Class clazzParam : clazzParams) {
					String cp=clazzParam.getName();
					String cp1=cp.substring(cp.lastIndexOf('.')+1);
					sb.append(cp1+",");	
					}
					sb.deleteCharAt(sb.length()-1);//去掉最後的那個,
					sb.append(')');
				}
				System.out.println(sb);	
			}
			System.out.println("----------第三步:查看此類中的所有方法----------------");
			Method[] methods=clazzProxy.getMethods();
			//循環得到所有的方法
			//打印的函數形式爲:函數名(參數類型,參數類型)
			for (Method method : methods) {
				String sc=method.getName();//com.sun.proxy.$Proxy0
				String sc1=sc.substring(sc.lastIndexOf('.')+1);//$Proxy0
				StringBuilder sb=new StringBuilder();
				//將獲得的構造函數名加入到容器中。
				sb.append(sc1);
				sb.append('(');
				//獲取此構造函數中的參數類型
				Class[] clazzParams=method.getParameterTypes();
				if (clazzParams!=null&&clazzParams.length!=0) {
					for (Class clazzParam : clazzParams) {
					String cp=clazzParam.getName();
					//將前面的前綴去掉,只打印最後的類型
					String cp1=cp.substring(cp.lastIndexOf('.')+1);
					sb.append(cp+",");	
					}
					sb.deleteCharAt(sb.length()-1);//去掉最後的那個,
					sb.append(')');
				}
				else
					sb.append(')');
				System.out.println(sb);	
			}
		}	
	}
---------------------------------------------------------------------------
創建動態類的實例對象及調用其方法

System.out.println("----------第四步:創建動態類對象---------------");
	//第一種方法來創建動態代理類對象
		//得到對應的構造函數
		Constructor constructor=clazzProxy.getConstructor(InvocationHandler.class);
		//由於InvocationHandler是一個接口,所以需要有一個類實現它才能創建它的對象
		class MyInvocationHandler implements InvocationHandler{
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				// TODO Auto-generated method stub
				return null;
			}	
		}
		//創建對象
		Collection proxy=(Collection)constructor.newInstance(new MyInvocationHandler());
		System.out.println(proxy);
	//第二種方法來創建動態代理類對象
		//或者用匿名內部類的形式
		Constructor constructor=clazzProxy.getConstructor(InvocationHandler.class);
		Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){

			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				return null;
			}
			
		});
	//第三種方法來創建動態代理類對象
		//通過方法newProxyInstance(接口類的類加載器在,實現的接口的字節碼,傳入InvocationHandler對象)
		//來直接一步創建出動態代理類
		Object proxy3 = Proxy.newProxyInstance(
				Collection.class.getClassLoader(),
				new Class[]{Collection.class},
				new InvocationHandler(){
				//複寫InvocationHandler中的invoke方法,
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						long beginTime = System.currentTimeMillis();
						Object retVal = method.invoke(target, args);
						long endTime = System.currentTimeMillis();
						System.out.println(method.getName() + " running time of " + (endTime - beginTime));
						return retVal;				
					}
				}
				);
---------------------------------------------------------------------------------------
分析動態代理類對象操作方法的原理以及InvocationHandler對象的運行原理
動態生成的類實現了Collection接口(可以實現若干接口),生成的類有Collection接口中的所有方法
和一個接受InvocationHandler參數的構造方法
1、此構造方法爲$Proxy0(InvocationHandler handler)
{
this.handler=handler;
}
2、對於調用方法size()時其size()方法的內部爲
int size()
{
return handler.invoke(this,this.getClass().getMethod("size"),null);
}
void clear()
{
handler.invoke(this,this.getClass().getMethod("clear"),null);
}
3、所以每次代理調用方法時都會調用一次InvocationHandler中的invoke方法,在此invoke的
代碼裏來添加想要添加的內容,並調用目標的相同的方法。從而實現添加功能即代理的功能。

import java.lang.reflect.Method;
	import java.lang.reflect.Proxy;
	import java.util.ArrayList;
	import java.util.Collection;
	public class ProxyTest {
		public static void main(String[] args) throws Exception{
			Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
			Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
			//傳入一個目標類對象
			final ArrayList target = new ArrayList();	
			//通過方法newProxyInstance(接口類的類加載器在,實現的接口的字節碼,傳入InvocationHandler對象)
			//來直接一步創建出動態代理類
			Collection proxy3 = (Collection)Proxy.newProxyInstance(
					Collection.class.getClassLoader(),
					new Class[]{Collection.class},
					new InvocationHandler(){
					//複寫InvocationHandler中的invoke方法,此方法中傳入的參數爲:代理類對象proxy,
					//調用代理類的method方法,以及此方法中需要傳入的參數args
						public Object invoke(Object proxy, Method method, Object[] args)
								throws Throwable {
							//加入其他的功能
							long beginTime = System.currentTimeMillis();
							//調用目標的method方法,此方法作用於目標target對象上,參數爲args
							Object retVal = method.invoke(target, args);
							long endTime = System.currentTimeMillis();
							System.out.println(method.getName() + " running time of " + (endTime - beginTime));
							return retVal;				
						}
					}
					);
					proxy3.add("zxx");//每寫一次add方法就會調用一次invoke方法,此時invoke方法中的參數
					proxy3.add("lhm");//分別對應proxy3,add方法和"zxx"
					proxy3.add("bxd");
					System.out.println(proxy3.size());//結果爲3.
		}
	}
-----------------------------------------------------------------------------------------------
總結分析動態代理類的設計原理與結構
即:客戶端調用代理->代理的構造方法傳遞一個handler對象->客戶端調用代理的方法->代理將客戶端的調用請求
發給handler對象->handler對象通過調用invoke又把請求發給目標類中的對應的方法。
也就是:客戶端調用代理類的方法,代理類再調用目標類的對應的方法。

-------------------------ASP.Net+Unity開發、.Net培訓、期待與您交流!--------------------------


詳情請查看:http://edu.csdn.net


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章