天天看點

黑馬程式員-代理(高新技術)

---------------------- ASP.Net+Android+IOS開發、.Net教育訓練、期待與您交流! ----------------------

OOP:面向對象的程式設計

AOP:面向方面的程式設計

Java虛拟機可以在運作期間動态生成類的位元組碼,這種動态生成的類往往被用作代理類,即動态代理類

Java虛拟機生成的動态類必須實作一個或多個接口(生成的動态類中的方法全部來自接口),是以,JVM生成的動态類隻能用作具有相同接口的類的代理

如果一個目标類沒有實作接口,那麼也想生成代理,那麼該如何做?

答:CGLIB庫可以動态生成一個類的子類,一個類的子類也可以用作該類的代理,是以,如果要為一個沒有實作接口的類動态生成動态代理類,那麼可以使用CGLIB庫

代理類的各個方法中通常除了要調用目标相對應方法和對外傳回目标傳回的結果外,還可以在代理方法中的如下四個位置加上系統功能代碼

(1):在調用目标方法之前

(2):在代用目标方法之後

(3):在調用目标方法前後

(4):在處理目标方法異常的catch塊中(重點了解)

示例:

class X
{
     void sayHello()
     {
         System.out.println(“hello”);
     }
}

XProxy
{
     X x = null;
     XProxy(X x)//代理類的構造參數必須接收參數
     {
        this.x = x;
}

     void sayHello()
     {
         starttime;//擷取開始時系統時間
         x.sayHello();
         endtime();//擷取結束時系統時間
     }
}
           

上述就使用了代理類,為目标X類加上了一段系統功能,用戶端加載運作的時候就調用XProxy類就可以了

動态建立代理類

Proxy類提供了一個方法:傳回代理類的位元組碼對象,并向其提供類加載器和接口數組

static Class<?>getProxyClass(ClassLoader loader, Class<?>... interfaces)

每一個位元組碼都必須具有一個類加載器加載進記憶體,如果沒有,則給指定一個

需求:使用java虛拟機動态生成代理類

列出代理類中的所有構造方法和參數類型

列出代理類中定義的所有方法和參數類型

代碼示例:

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
		//取得代理類的位元組碼對象,Proxy.getProxyClass()是一個靜态方法,通過類名Proxy調用
		@SuppressWarnings("rawtypes")
		Class clazzProxy1 = 
Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
//用JVM建立代理類需要傳入一個類加載器,和一個接口
		//得到該代理類所屬的類的名字
		System.out.println(clazzProxy1.getName());
		
		showConstructor(clazzProxy1);
		showMethods(clazzProxy1);
	}

	@SuppressWarnings("rawtypes")
	private static void showConstructor(Class clazzProxy1) {
		System.out.println("begin Constructors------------");
		//取得所有的構造方法,傳回一個數組
		Constructor[] Constructor = clazzProxy1.getConstructors();
		//周遊數組,取出構造方法
		for(Constructor constructors:Constructor)
		{
			//得到該構造方法的名字
			String name = constructors.getName();
			//把構造方法的名字添加到StringBuilder
			StringBuilder sconstructors = new StringBuilder(name);
			sconstructors.append("(");
			//取得構造方法的參數清單,傳回一個裝有參數清單的數組
			Class[] clazzParams = constructors.getParameterTypes();
			//周遊該參數數組,取得參數
			for(Class clazzParam:clazzParams)
			{//參數的的名字添加到StringBuilder
				sconstructors.append(clazzParam.getName()).append(",");
			}
			if(clazzParams!=null&&clazzParams.length != 0)
			{
				//去掉最後一個“,”
				sconstructors.deleteCharAt(sconstructors.length()-1);
			}
			sconstructors.append(")");
			System.out.println(sconstructors.toString());
		}
	}
	
	@SuppressWarnings({ "rawtypes", "rawtypes", "rawtypes" })
	private static void showMethods(Class clazzProxy1) {
		System.out.println("begin Methods------------");
		//取得所有的構造方法,傳回一個數組
		Method[] method = clazzProxy1.getMethods();
		//周遊數組,取出構造方法
		for(Method methods:method)
		{
			//得到該構造方法的名字
			String name = methods.getName();
			//System.out.println(name);
			//把構造方法的名字添加到StringBuilder
			StringBuilder sMethods = new StringBuilder(name);
			sMethods.append("(");
			//取得構造方法的參數清單,傳回一個裝有參數清單的數組
			Class[] clazzParams = methods.getParameterTypes();
			//周遊該參數數組,取得參數
			for(Class clazzParam:clazzParams)
			{//參數的的名字添加到StringBuilder
				sMethods.append(clazzParam.getName()).append(",");
			}
			if(clazzParams!=null&&clazzParams.length != 0)
			{
				//去掉最後一個“,”
				sMethods.deleteCharAt(sMethods.length()-1);
			}
			sMethods.append(")");
			System.out.println(sMethods.toString());
			
		}
	}
}
           

輸出結果:

com.sun.proxy.$Proxy0

beginConstructors------------

com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler,)

begin Methods------------

add(java.lang.Object)

remove(java.lang.Object)

equals(java.lang.Object)

toString()

…顯示一部分結果

用JVM建立動态代理類的要點

(1):Proxy.getProxyClass()方法是一個通過類名調用的靜态方法,能夠傳回一個代理類的位元組碼對象,這裡需要傳入兩個參數,以為在記憶體中動态建立位元組碼,需要讓該位元組碼具有一個類加載器,這是java規定的,每個位元組碼都是由一個加載器加載器加載進來的,如果沒有,就指定一個,除此之外,還要傳入一個接口類型,告訴虛拟機這個代理類實作了哪個接口

建立動态類的執行個體對象

注意事項:

(1)不能直接通過classProxy1.newStance()建立對象,因為這種建立對象的方式會調用類的無參構造方法,因為代理類不存在無參的構造方法,是以編譯會報錯(傳入的參數是為了關聯目标類,和指定目标類的方法和該方法接受的參數)

必須先得到構造方法,再指定參數調用構造方法:

Constructor con = clazzProxy1.getConstroctor(InvocationHandle.class) 

附:InvocationHandle.class是參數的類型,但是InvocationHandle是一個接口

如果要傳入InvocationHandle對象參數去建立代理類的執行個體對象,那麼必須要先建立一個類實作InvocationHandle接口并實作裡面的方法(可以用最簡單的實作,空的方法體)

class invo implements InvocationHandle

{

Invoke(Object proxy,Method method,Object[]args)

{}//最簡單的實作是空方法體

}

附:Invoke(Object proxy,Method method,Object[] args)參數的意義

Object proxy:表示調用的是哪個代理對象

Method method:表示哪個方法時要的是代理對象的哪個方法

Object[] args:表示的是調用要傳入的對象,有可能是多個對象,用數組接收

但是也可以直接以InvocationHandle的匿名内部類形式傳入參數

Constructor con = clazzProxy1.getConstroctor(new InvocationHandle()
{
     Invoke(Object proxy,Method method,Object[] args)
     {
          return null;
     }
})
           

上述方法建立對象是分開兩步來寫的,此外,java還提供了一個一步到位,建立代理對象的方法,使用一個Proxy的靜态方法(傳入參數類加載器,實作的接口,InvocationHandler)

newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h);

示例:

Collection proxy = 
(Collection)Proxy. newProxyInstance(Collection.getClassLoader(), 
new Class[]{Collection},//可能是有多個接口,用數組
new InvocationHandle()
{
Invoke(Object proxy,Method method,Object[] args)
{
return null;
}
})
           

分析動态生成類的内部代碼

Proxy implements Collection
{
    InvocationHandle handler
public Proxy(InvocationHandle handler)
{
        this. Handler = handler; 
}
}

Proxy.add(“abc”);對應的是要代理的目标對象,add方法,往方法裡所傳參數
Class Proxy implements Collection
{
    InvocationHandle handler
public Proxy(InvocationHandle handler)
{
        this. handler = handler; 
}
add(Object obj)
{
    return handler.invoke(Object proxy,Method method,Object[] args);
}
}  
           
黑馬程式員-代理(高新技術)

---------------------- ASP.Net+Android+IOS開發、.Net教育訓練、期待與您交流! ----------------------

詳細請檢視:http://edu.csdn.net

繼續閱讀