使用反射生成JDK動態代理

使用Proxy和InvocationHandler建立動態代理

Proxy提供了用於建立動態代理類和代理對象的靜態方法,他也是全部動態代理類的父類。若是在程序中爲一個或多個接口動態的生成實現類,就可使用Proxy來建立動態代理類;Proxy提供了以下兩種方法來建立動態代理類和動態代理實例:

1,staic Class getProxyClass(ClassLoader loader,Class .... interfaces):建立一個動態代理類所對應的Class對象,該代理類將實現interfaces所指定的多個接口。loader參數指定生成的動態代理類的類加載器。java

2,static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h);直接建立一個動態代理對象,該代理對象的實現類實現了interfaces指定的系列接口,執行代理對象的每一個方法時都會被替換執行InvocationHandler對象的invoke方法。編程

實際上,即便採用第一個方法生成代理類以後,若是程序須要經過該代理類來建立對象,依然須要傳入一個InvocationHandler對象。無論是執行代理對象的任何方法,實際上都是執行InvocationHandler對象的invoke方法。下面示範使用Proxy和InvocationHandler來生成動態代理對象

1,代理接口框架

package www.com.lining.test1;

public interface Person {

	void walk();

	void sayHello(String name);

}

2,實現類ide

package www.com.lining.test1;

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

public class MyInvokationHandler implements InvocationHandler {

	/**
	 * 執行動態代理對象的全部方法時,都會被替換成invoke方法</br>
	 * proxy:表明代理的對象 mothod:正在執行的方法 args:調用目標方法時的實參
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("-----正在執行的方法:" + method);
		if (args != null) {
			System.out.println("下面是執行該方法時傳入的實參爲:");
			for (Object val : args) {
				System.out.println(val);
			}
		} else {
			System.out.println("調用該方法沒有實參!");
		}
		return null;
	}

}

3,測試類測試

package www.com.lining.test1;

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

public class ProxyTest {

	public static void main(String[] args) {
		// 建立一個InvocationHandler對象
		InvocationHandler handler = new MyInvokationHandler();
		// 使用指定的InvocationHandler來生成一個動態代理對象
		Person p = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[] { Person.class },
				handler);// 得到代理接口的加載器,建立接口的class對象,代理接口的實現類對象。
		// 調用動態代理對象的walk()和sayhello()方法
		p.walk();
		p.sayHello("孫悟空");
	}

}

4,運行結果this

-----正在執行的方法:public abstract void www.com.lining.test1.Person.walk()
調用該方法沒有實參!
-----正在執行的方法:public abstract void www.com.lining.test1.Person.sayHello(java.lang.String)
下面是執行該方法時傳入的實參爲:
孫悟空

實際上,在廣泛的編程過程當中,確實無需使用動態代理,可是在編寫框架或底層基礎代碼時,動態代理的做用就很是大了。編碼

動態代理和AOP

根據前面介紹的方法來建立動態代理,咱們很難看出這種動態代理的優點,下面咱們將使用一種實用的代理機制。
在開發中咱們會經常碰見相同重複的代碼,經常咱們會使用複製粘貼的方法來解決,可是在後期維護中咱們會很頭痛。好比一個相同的代碼塊在多個調用方法中出現,這樣咱們可使用動態代理的方法來解決這一問題,下降複用。

1,咱們先提供一個Dog接口,定義兩個方法就好。代理

package www.com.lining.test1;

/**
 * 要被代理的對象
 * 
 * @author YOONA
 *
 */
public interface Dog {

	void info();

	void eat();
}

2,這裏使用的是AOP代理不是jdk代理,因此咱們不要直接使用Proxy爲給接口建立動態代理對象,而是爲該接口提供實現類code

package www.com.lining.test1;

public class GunDog implements Dog {

	@Override
	public void info() {
		System.out.println("你是一隻獵狗");

	}

	@Override
	public void eat() {

		System.out.println("你吃shi的很香");
	}

}

3,這裏咱們須要提供一個前面提起的一個代碼塊須要被重複使用,咱們不要複製粘貼,而是使用代理來調用不一樣方法可是是同一個代碼塊的方法。對象

package www.com.lining.test1;

public class DogUtil {

	public void method1() {
		System.out.println("=====模擬第一個通用方法======");
	}

	public void method2() {
		System.out.println("=====模擬第二個通用方法======");
	}
}

4,咱們藉助Proxy的InvocationHandler就能夠實現調用Dog接口裏的方法,這時程序就會自動調用相同的代碼塊method1,method2。這裏的做用就是當使用代理對象時就會執行該類中的invoke方法。執行動態代理對象的全部方法時,都會被替換成執行Invoke方法。

package www.com.lining.test1;

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

public class MyInvokationHandler2 implements InvocationHandler {

	private Object target;

	public void setTarget(Object t) {
		this.target = t;
	}
//重寫Invoke方法
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

		DogUtil du = new DogUtil();
		du.method1();
		Object res = method.invoke(target, args);
		du.method2();
		return null;
	}

}

5,根據上面的程序咱們能夠定義target爲被代理的實例,這裏咱們創建一個代理該對象的工廠。

package www.com.lining.test1;

import java.lang.reflect.Proxy;

public class MyProxyFactory {

	public static Object getProxy(Object t) {

		// 建立一個實現InvocationHandler的類對象
		MyInvokationHandler2 handler2 = new MyInvokationHandler2();
		// 爲MyInvokationHandler2設置setTarget;
		handler2.setTarget(t);
		return Proxy.newProxyInstance(GunDog.class.getClassLoader(), t.getClass().getInterfaces(), handler2);
	}

}

6,調用MyProxyFactory對象的getProxy(代理實例)方法,來建立動態代理對象。

package www.com.lining.test1;

public class TestAOP {

	public static void main(String[] args) {

		Dog t = new GunDog();
		Dog d = (Dog) MyProxyFactory.getProxy(t);
		d.info();
		d.eat();
	}

}

7,輸出結果

=====模擬第一個通用方法======
你是一隻獵狗
=====模擬第二個通用方法======
=====模擬第一個通用方法======
你shi吃的很香
=====模擬第二個通用方法======

總結:

1,上面的動態代理工廠提供了一個getProxy()方法,該方法爲target對象生成一個動態代理對象,這個代理對象與target實例實現了相同的接口,因此具備相同的public方法,也就是說動態代理對象能夠當成target對象使用。當程序調用動態代理對象的指定方法時,實際上將變成執行MyInvocationHandler對象的invoke方法。執行步驟以下:

  • 建立DogUtil實例。
  • 執行DogUtil實例的method1方法。
  • 使用反射以target做爲調用者執行info方法。
  • 執行DogUtil實例的method2方法。

從上面的過程當中咱們能夠看出:當使用動態代理對象來替代target對象時,代理對象的方法就實現了前面的要求--程序執行info,eat方法時既能插入method一、method2通用方法,但GunDog的方法中又沒有以硬編碼方式調用method一、method2通用方法。

2,執行步驟: 程序執行dog的info和eat方法時,實際執行的是DogUtil的方法method1,再執行target對象的info和eat,最後執行DogUtil的method2方法。(AOP面向切面編程)

3,AOP代理方法示意圖:

相關文章
相關標籤/搜索