動態代理和AOP02

如要轉載請標明作者zjrodger和出處:http://blog.csdn.net/zjrodger/,謝謝微笑

                                                        筆記目錄
· 分析JVM動態生成的類
(·讓JVM先創建動態類,再創建其實例對象一般方法
1. 創建一個實現了Collection接口的動態代理類(並不創建其實例對象)。
2. 實現Collection接口,創建一個動態代理類,並且創建該動態類的對象。
(·) 讓JVM創建動態類及其實例對象快速簡便方法
(·) 總結讓JVM創建動態代理類及其實例對象
(·) 分析InvocationHandler對象的運行原理和動態代理的運行原理
1. 猜想分析動態生成的類的內部代碼
2. 動態代理類的工作原理圖
3. 實際開發中的動態代理類的實現(重要



· 分析JVM動態生成的類
(·讓JVM先創建動態類,再創建其實例對象一般方法
第一步:創建動態代理類。新創建的代理類的類名一般是:$Proxy0。 
第二部:創建動態類的實例對象。
1. 創建一個實現了Collection接口的動態代理類(並不創建其實例對象)。
【實現功能】
(1)通過實現某個接口,比如java.util.Collection,創建一個動態的代理類。 
(2)該接口可以由config.properties配置文件指定。
(3)新創建的動態的代理類會將該代理類的構造器和普通方法,以一個列表的形式打印出來。
【程序代碼】
(1)config.properties配置文件中的內容:keyName01=java.util.Collection
(2)Java代碼

package com.zjrodger.day3;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.Properties;

/**
 * 程序功能: 
 * 1. 通過實現某個接口,比如java.util.Collection,創建一個動態的代理類。
 * 2. 該接口可以由config.properties配置文件指定。
 * **/
public class ProxyTest{
	
	private static String interfaceName = null;
	
	//在靜態初始化塊兒中讀取配置文件config.properties中的類名。
	static{
		InputStream inStream = null;
		Properties prop = new Properties();	
		try {
			inStream = ProxyTest.class.getClassLoader().getResourceAsStream("com/zjrodger/day3/config.properties");
			prop.load(inStream);
			ProxyTest.interfaceName = prop.getProperty("keyName01");			
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("讀取配置文件config.properties失敗!!");
		} finally{
			try {
				inStream.close();				
			} catch (IOException e) {
				e.printStackTrace();
				System.out.println("關閉配置文件config.properties失敗!!");
			}
			inStream = null;
		}
	}
	
	public static void main(String[] args){	
		Class<?> clazzInterface = null;
		
		try {
			clazzInterface = Class.forName(ProxyTest.interfaceName);			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			System.out.println("加載ProxyTest.interfaceName失敗!!");
		}
		
		System.out.println(clazzInterface.getName());
		
		// 通過實現Collection接口,創建一個動態的代理類。
		Class<?> clazzMyProxyName01 = Proxy.getProxyClass(clazzInterface.getClassLoader(), clazzInterface);
		
		System.out.println("-----------------動態代理類的構造方法列表-------------");

		//預期目標: $Proxy0(java.lang.reflect.InvocationHandler)
		Constructor[] constructors = clazzMyProxyName01.getConstructors();
		for(Constructor oneConstructor: constructors){			
			StringBuilder sBuilder = new StringBuilder(oneConstructor.getName()+'(');
			
			Class[] parameterTyes = oneConstructor.getParameterTypes();
			for(Class oneParamType: parameterTyes){
				sBuilder.append(oneParamType.getName());
				sBuilder.append(',');
			}
			if(parameterTyes != null && parameterTyes.length != 0){
				sBuilder.deleteCharAt(sBuilder.length()-1);
			}
			
			sBuilder.append(')');	
			System.out.println(sBuilder.toString());
		}			
		System.out.println("\n");
		
		
		System.out.println("-----------------動態代理類的普通方法列表-------------");
		Method[] methods = clazzMyProxyName01.getMethods();
		for(Method oneMethod: methods){			
			StringBuilder sBuilder = new StringBuilder(oneMethod.getName()+'(');
			
			Class[] parameterTyes = oneMethod.getParameterTypes();
			for(Class oneParamType: parameterTyes){
				sBuilder.append(oneParamType.getName());
				sBuilder.append(',');
			}
			if(parameterTyes != null && parameterTyes.length != 0){
				sBuilder.deleteCharAt(sBuilder.length()-1);
			}			
			sBuilder.append(')');	
			System.out.println(sBuilder.toString());
		}		
	}
}

【運行結果】

java.util.Collection
-----------------動態代理類的構造方法列表-------------
$Proxy0(java.lang.reflect.InvocationHandler)
-----------------動態代理類的普通方法列表-------------
add(java.lang.Object)
hashCode()
clear()
equals(java.lang.Object)
toString()
contains(java.lang.Object)
isEmpty()
addAll(java.util.Collection)
iterator()
size()
toArray([Ljava.lang.Object;)
toArray()
remove(java.lang.Object)
containsAll(java.util.Collection)
removeAll(java.util.Collection)
retainAll(java.util.Collection)
isProxyClass(java.lang.Class)
getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)
getInvocationHandler(java.lang.Object)
newProxyInstance(java.lang.ClassLoader,[Ljava.lang.Class;,java.lang.reflect.InvocationHandler)
wait()
wait(long,int)
wait(long)
getClass()
notify()
notifyAll()

2. 實現Collection接口,創建一個動態代理類,並且創建該動態類的對象。
【實現功能】
通過實現java.util.Collection接口,創建一個動態的代理類,並且創建該類的實例對象。 
方式一:創建一個內部類,該內部類實現了InvocationHandler接口。
方式二:通過匿名內部類來創建動態代理類的實例對象。
【程序代碼】
(1)config.properties配置文件中的內容:keyName01=java.util.Collection
(2)Java代碼
package com.zjrodger.day3;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Properties;

/**
 * 程序功能: 
 * 1. 通過實現某個接口,比如java.util.Collection,創建一個動態的代理類。
 * 2. 該接口可以由config.properties配置文件指定。
 * **/
public class ProxyTest{
	
	private static String interfaceName = null;
	
	//在靜態初始化塊兒中讀取配置文件config.properties中的類名。
	static{
		InputStream inStream = null;
		Properties prop = new Properties();	
		try {
			inStream = ProxyTest.class.getClassLoader().getResourceAsStream("com/zjrodger/day3/config.properties");
			prop.load(inStream);
			ProxyTest.interfaceName = prop.getProperty("keyName01");			
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("讀取配置文件config.properties失敗!!");
		} finally{
			try {
				inStream.close();				
			} catch (IOException e) {
				e.printStackTrace();
				System.out.println("關閉配置文件config.properties失敗!!");
			}
			inStream = null;
		}
	}
	
	public static void main(String[] args){	
		Class<?> clazzInterface = null;
		
		try {
			clazzInterface = Class.forName(ProxyTest.interfaceName);			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			System.out.println("加載ProxyTest.interfaceName失敗!!");
		}
		
		System.out.println(clazzInterface.getName());
		
		// 通過實現Collection接口,創建一個動態的代理類。
		Class<?> clazzMyProxyClassName01 = Proxy.getProxyClass(clazzInterface.getClassLoader(), clazzInterface);
		
		
		/**
		 * 創建該動態代理類的實例對象。
		 * **/		
		try {
			//方式一:創建一個內部類,該內部類實現了InvocationHandler接口。
			class MyInvocationHandler implements InvocationHandler{
				public Object invoke(Object proxy, Method method, Object[] args)
						throws Throwable {
					return null;
				}			
			}			
			Constructor constructor = clazzMyProxyClassName01.getConstructor(InvocationHandler.class);

			//創建動態代理類的實例對象。			
			Collection proxyInstance01 = (Collection)constructor.newInstance(new MyInvocationHandler());
			System.out.println(proxyInstance01.toString());
			proxyInstance01.clear();

			
			//方式二:通過匿名內部類來創建動態代理類的實例對象。
			Collection proxyInstance02 = (Collection)constructor.newInstance(new InvocationHandler(){
				public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
					return null;
				}			
			});
						
			System.out.println("The size is "+proxyInstance02.size());
			
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}				
	}
}

(·) 讓JVM創建動態類及其實例對象快速簡便方法
【實現功能】
通過實現java.util.Collection接口,創建一個動態的代理類,並且創建該類的實例對象。 
方式三快速簡便方法的代碼結構:
 Foo f = (Foo) Proxy.newProxyInstance( Foo.class.getClassLoader(),new Class[] { Foo.class },handler);
【程序代碼】
(1)config.properties配置文件中的內容:keyName01=java.util.Collection
(2)Java代碼
package com.zjrodger.day3;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Properties;

/**
 * 程序功能: 
 * 1. 通過實現某個接口,比如java.util.Collection,創建一個動態的代理類。
 * 2. 該接口可以由config.properties配置文件指定。
 * **/
public class ProxyTest{
	
	private static String interfaceName = null;
	
	//在靜態初始化塊兒中讀取配置文件config.properties中的類名。
	static{
		InputStream inStream = null;
		Properties prop = new Properties();	
		try {
			inStream = ProxyTest.class.getClassLoader().getResourceAsStream("com/zjrodger/day3/config.properties");
			prop.load(inStream);
			ProxyTest.interfaceName = prop.getProperty("keyName01");			
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("讀取配置文件config.properties失敗!!");
		} finally{
			try {
				inStream.close();				
			} catch (IOException e) {
				e.printStackTrace();
				System.out.println("關閉配置文件config.properties失敗!!");
			}
			inStream = null;
		}
	}
	
	public static void main(String[] args){	
		Class<?> clazzInterface = null;
		
		try {
			clazzInterface = Class.forName(ProxyTest.interfaceName);			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			System.out.println("加載ProxyTest.interfaceName失敗!!");
		}
		
		System.out.println(clazzInterface.getName());
		
		// 通過實現Collection接口,創建一個動態的代理類。
		Class<?> clazzMyProxyClassName01 = Proxy.getProxyClass(clazzInterface.getClassLoader(), clazzInterface);
		
		
		/**
		 * 創建該動態代理類的實例對象。
		 * **/		
		try {
			//方式三:讓JVM創建動態類及其實例對象的快速簡便方法。
			Collection proxyInstance03 = (Collection) Proxy.newProxyInstance(
					ProxyTest.class.getClassLoader(), 
					new Class[]{java.util.Collection.class}, 					
					new InvocationHandler(){						
						//定義一個目標類,注意區別它和Proxy代理類的關係;
						//另外,目標類和Proxy代理類都實現了相同的接口。
						ArrayList target = new ArrayList();						
						public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
							
							long beginTime = System.currentTimeMillis();
							try {
								Thread.sleep(1000);
							} catch (Exception e) {
								e.printStackTrace();
							}
							Object returnedValue = method.invoke(target, args);
							long endTime = System.currentTimeMillis();
							System.out.println("代理類實例的代理方法"+method.getName()+"()執行的時間跨度爲: "+(endTime - beginTime));
							return returnedValue;
						}			
					}
			);
			
			proxyInstance03.add("zjrodger01");
			proxyInstance03.add("Coffee");
			proxyInstance03.add("Zhuxinyue");
			System.out.println("The size is "+proxyInstance03.size());
			
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}				
	}
}

(·) 總結讓JVM創建動態代理類及其實例對象
讓JVM創建動態類及其實例對象,需要爲其提供哪些信息?
(1) 生成的動態類中有哪些方法,通過讓其實現相應的接口來告知JVM生成的動態類中所包含的各個方法。
(2) 新創建的類的字節碼必須由一個關聯的類加載器的對象。
(3) 創建動態類的實例對象時,還必須向其構造其中傳入一個InvocationHandler。

(·) 分析InvocationHandler對象的運行原理和動態代理的運行原理
1. 猜想分析動態生成的類的內部代碼
(1) 動態生成的類實現了Collection接口(可以實現若干個接口),生成的類有Collection接口中的所有方法和一個如下接受InvocationHandler參數的構造方法。
(2) 構造方法接受一個InvocationHandler對象,接收對象了要幹什麼用呢?該方法內部的代碼會是怎樣的呢?
(3) 實現Collection接口的動態類中的各個方法的代碼又是怎樣的呢?
InvocationHandler接口中定義的invoke()方法接受的單個參數又是什麼意思? 圖解如下說明:
註釋描述:Client程序調用objProxy.add("add")方法時,涉及三個要素:objProxy對象,add方法,"abc"參數
Class Proxy${
    add(Object object){
        return handler.invoke(Object proxy, Method method, Object[] args);
    }
}
(4) 分析先前打印動態類的內部對象時,結果爲什麼會是null呢?
   動態代理類中的一部分方法繼承自java.lang.Object類,當調用hashCode()方法, equals()方法和toString()方法時,這些方法會被推送給目標類的方法;若調用剩餘的其他方法,比如getClass()方法,Proxy類就會有自己的實現,即不會將方法交給Handler。

2. 動態代理類的工作原理圖

    在log()(劃紅線的地方)傳遞一個對象,這樣就可以執行對象中的方法,從而避免了硬編碼把代碼寫死的狀態,方便以後靈活的應用。
這樣就是AOP(面向切面變成)的好處,即,將切面的代碼用對象的方式進行封裝,然後用對象的方式傳遞給InvocationHandler,這樣相當於執行了切面的代碼。

3. 實際開發中的動態代理類的實現(重要
    在實際的開發中,向InvocationHandler對象實際傳入的對象應該有兩個:一個是target對象,另一個是系統的切面對象(Advice接口的實現類對象)。
3.1)具體實現思路
(1)定義並且獲得target類實例。
(2)要定義一個接口,其名字叫做Advice(Spring中的術語),。
    將系統切面代碼(負責安全,日誌,系統時間等具有輔助功能的代碼)封裝到接口Advice中去,然後獲得Advice接口的實現類和實現類對象。
(3)編寫生成Proxy對象的方法,假設其名爲:Object myGetProxyInstance( Object targetAdvice advice )

3.2)實現代碼
【功能描述】
    利用動態代理功能,向ArrayList類(target類)中添加若干元素。
    在添加元素期間,打印系統輔助的切面代碼的信息。
【程序代碼】
生成動態代理對象的核心類
package com.zjrodger.aopframework02;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxy {
	/**
	 * 1.根據傳入的targe對象和advice對象,生成相應的Proxy對象。
	 * 2.代碼改造:
	 *   也可以將targe對象和advice對象定義爲DynamicProxy類的靜態類屬性,這樣,在創建Proxy對象之前,
	 *   可以通過靜態的setter()和getter()方法設置target對象和advice對象,這樣,
	 *   當調用myNewProxyInstance()方法時,就不用將target對象和advice對象作爲參數傳入到方法中了。
	 * **/
	public static Object myNewProxyInstance(final Object target, final Advice advice){
		
		Object oneProxyInstance = Proxy.newProxyInstance(
				target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(), 
				new InvocationHandler(){
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {						
						advice.beforeMethod();  //系統輔助功能的切面代碼01
						
						//核心業務代碼						
						Object returnedValue = method.invoke(target, args);								
						System.out.println("核心方法\""+method.getName()+"()\"執行完畢,其返回值:"+returnedValue);						
						
						Thread.sleep(1500);
						advice.afterMethod();   //系統輔助功能的切面代碼02
						return returnedValue;
					}
				}
		);		
		return oneProxyInstance;
	}
}

②Advice接口
package com.zjrodger.aopframework02;

public interface Advice {
	public void beforeMethod();
	public void afterMethod();
}

③Advice接口的實現類MyAdvice類
package com.zjrodger.aopframework02;

import java.text.DateFormat;
import java.util.Date;

public class MyAdvice implements Advice {

	private DateFormat df = DateFormat.getTimeInstance(DateFormat.MEDIUM);
	
	public void afterMethod() {
		System.out.println("執行核心代碼之後,執行切面代碼02,時間爲:"+df.format(new Date()));
		System.out.println("\n");
	}

	public void beforeMethod() {
		System.out.println("執行核心代碼之前,執行切面代碼01,時間爲:"+df.format(new Date()));
	}
}

④程序啓動入口類
package com.zjrodger.aopframework02;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class DynamicProxyTestLauncher {
	public static void main(String[] args) {
		//定義一個taget類
		ArrayList target = new ArrayList();
		
		//定義一個advice類
		MyAdvice advice = new MyAdvice();
		
		//生成動態代理對象。
		Collection proxyObj = (Collection)DynamicProxy.myNewProxyInstance(target,advice);
		
		//調用新創建動態代理對象的方法。
		proxyObj.add("zjrodger");
		proxyObj.add("Lilin");
		System.out.println("Proxy對象的size屬性爲: "+proxyObj.size());

		//遍歷新建的Proxy對象中存放的元素。
		for(Iterator it = proxyObj.iterator(); it.hasNext();){
			System.out.println(it.next());
		}
	}
}

【結果演示】
執行核心代碼之前,執行切面代碼01,時間爲:14:37:59 
核心方法"add()"執行完畢,其返回值:true 
執行核心代碼之後,執行切面代碼02,時間爲:14:38:00 

執行核心代碼之前,執行切面代碼01,時間爲:14:38:00 
核心方法"add()"執行完畢,其返回值:true 
執行核心代碼之後,執行切面代碼02,時間爲:14:38:02 

執行核心代碼之前,執行切面代碼01,時間爲:14:38:02 
核心方法"size()"執行完畢,其返回值:2 
執行核心代碼之後,執行切面代碼02,時間爲:14:38:03 

Proxy對象的size屬性爲: 2 
執行核心代碼之前,執行切面代碼01,時間爲:14:38:03 
核心方法"iterator()"執行完畢,其返回值:java.util.AbstractList$Itr@1dff3a2 
執行核心代碼之後,執行切面代碼02,時間爲:14:38:05 

zjrodger 
Lilin


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