第二十六部分_代理模式與動態代理詳解

與IoC相似的是,AOP也使用了一種設計模式,這種設計模式叫作代理模式。java

代理模式的做用是:爲其餘對象提供一種代理以控制對這個對象的訪問。設計模式

在某些狀況下,一個客戶不想或者不能直接引用另外一個對象,而代理對象能夠在客戶端和目標對象之間起到中介的做用。數組

掌握代理模式對於Spring AOP的學習是相當重要的,甚至比Spring AOP自己的學習還要重要。ide

代理模式通常涉及到的角色有:函數

  • 抽象角色:聲明真實對象和代理對象的共同接口
  • 代理角色:代理對象角色內部含有對真實對象的引用,從而能夠操縱真實對象,同時代理對象提供與真實對象相同的接口以便在任什麼時候刻都能代替真實對象。同時,代理對象能夠在執行真實對象操做時,附加其餘的操做,至關於對真實對象進行封裝
  • 真實角色:代理角色所表明的真實對象,是咱們最終要引用的對象

本身動手實現代理模式:post

抽象角色:Subject學習

package com.test.proxy;

public abstract class Subject
{
	public abstract void request();
}

真實角色:RealSubjectthis

package com.test.proxy;

public class RealSubject extends Subject
{
	@Override
	public void request()
	{
		System.out.println("from real subject");
	}
}

代理角色:ProxySubjectspa

package com.test.proxy;

public class ProxySubject extends Subject
{
	private RealSubject realSubject;
	
	@Override
	public void request()
	{
		this.preRequest();
		
		if(null == realSubject)
			realSubject = new RealSubject();
		
		realSubject.request();
		
		this.postRequest();
	}
	
	private void preRequest()
	{
		System.out.println("pre request");
	}
	
	private void postRequest()
	{
		System.out.println("post request");
	}
}

客戶端:設計

package com.test.proxy;

public class Client
{
	public static void main(String[] args)
	{
		Subject subject = new ProxySubject();
		
		subject.request();
	}
}

運行Client,輸出:

pre request
from real subject
post request
  • 由以上代碼能夠看出,客戶實際須要調用的是RealSubject類的request()方法,如今用ProxySubject來代理RealSubject類,一樣達到目的,同時還封裝了其餘方法(preRequest(), postRequest()),能夠處理一些其餘問題。
  • 另外,若是要按照上述的方法使用代理模式,那麼真實角色必須是事先已經存在的,並將其做爲代理對象的內部屬性。可是實際使用時,一個真實角色必須對應一個代理角色,若是大量使用會致使類的急劇膨脹;此外,若是事先並不知道真實角色,該如何使用代理呢?這個問題能夠經過Java的動態代理類來解決。

上面介紹的設計模式實際上叫作靜態代理,下面咱們介紹動態代理,它其實就是Spring AOP的底層實現。

動態代理類:

所謂Dynamic Proxy是這樣一種class:它是在運行時生成的class,在生成它時你必須提供一組interface給它,而後該class就宣稱它實現了這些interface。你固然能夠把該class的實例看成這些interface中的任何一個來使用。固然這個Dynamic Proxy其實就是一個Proxy,他不會替你作實質性的工做,在生成它的實例時你必須提供一個handler,由它接管實際的工做。

Java動態代理類位於java.lang.reflect包下,通常主要涉及到如下兩個類:

  • Interface InvocationHandler:該接口僅定義了一個方法
    • public Object invoke(Object proxy, Method method, Object[] args)
    • 在實際使用時,第一個參數obj通常是指代理類,method是被代理的方法,如上例中的request(),args爲該方法的參數數組。這個抽象方法在代理類中動態實現。
  • Proxy:該類即爲動態代理類,做用相似於上例中的ProxySubject,其中主要包含如下內容
    • protected Proxy(InvocationHandler h):構造函數,用於給內部的h賦值。
    • static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces):得到一個代理類,其中loader是類裝載器,interfaces是真實類所擁有的所有接口的數組。
    • static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h):返回代理類的一個實例,返回後的代理類能夠看成被代理類使用(可以使用被代理類在Subject接口中聲明過的方法)

下面咱們看下如何實如今程序中實現動態代理(在使用動態代理類時,咱們必須實現InvocationHandler接口):

被代理的類以及接口:

package com.test.dynamicproxy;

public interface Subject
{
	public void request();
}

----------------------------------------------------------------------------
package com.test.dynamicproxy;

public class RealSubject implements Subject
{
	public void request()
	{
		System.out.println("from real subject");
	}
}

代理類:

package com.test.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
 * 該代理類的內部屬性是Object類型,實際使用的時候經過該類的構造方法傳遞進來一個對象
 * 此外,該類還實現了invoke方法,該方法中的method.invoke其實就是調用被代理對象的將要
 * 執行的方法,方法參數是sub,表示該方法從屬於sub,經過動態代理類,咱們能夠在執行真實對象的方法先後
 * 加入本身的一些額外方法。
 *
 */
public class ProxySubject implements InvocationHandler
{
	private Object sub;
	
	public ProxySubject(Object object)
	{
		this.sub = object;
	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable
	{
		System.out.println("pre processing");
		
		method.invoke(sub, args);
		
		System.out.println("post processing");
		
		return null;
	}

}

客戶端:

package com.test.dynamicproxy;

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

public class Client
{
	public static void main(String[] args)
	{
		// 模擬從Spring配置文件中獲得的subject
		RealSubject subject = new RealSubject();
		
		InvocationHandler ih = new ProxySubject(subject);
	
		Class<?> clazz = subject.getClass();
		
		// 一次性生成代理
		Subject s = (Subject)Proxy.newProxyInstance(clazz.getClassLoader(), 
				clazz.getInterfaces(), ih);	
		
		s.request(); // 由InvocationHandler的invock()方法執行真正的調用	
	}
}

運行結果:

pre processing
from real subject
post processing

無論有多少具體的真實角色,代理能夠只有一個,這一個代理能夠用於處理全部的真實角色。

  • 經過這種方式,被代理的對象(RealSubject)能夠在運行時動態改變,須要控制的接口(Subject接口)能夠在運行時改變,控制的方式(ProxySubject類)也能夠動態改變,從而實現了很是靈活的動態代理關係。
  • 使用場合:
    • 調試
    • 遠程方法調用(RMI)
    • AOP

總結:動態代理的建立步驟(重要)

  • 建立一個實現接口InvocationHandler的類,它必須實現invoke方法
  • 建立被代理的類以及接口
  • 經過Proxy的靜態方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)建立一個代理
  • 經過代理調用方法

程序實踐:

package com.test.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Vector;

public class VectorProxy implements InvocationHandler
{
	private Object proxyobj;
	
	public VectorProxy(Object obj)
	{
		proxyobj = obj;
	}

	public static Object factory(Object obj)
	{
		Class<?> cls = obj.getClass();
		
		return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), new VectorProxy(obj));
	}
	
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable
	{
		System.out.println("before calling " + method);
		
		if(args != null)
			for(int i = 0; i < args.length; i++)
				System.out.println(args[i]);
		
		Object object = method.invoke(proxyobj, args);
		
		return object;
	}
	
	public static void main(String[] args)
	{
		
		List v = (List)factory(new Vector());
		
		v.add("hello");	
		v.add("world");
		System.out.println(v);
		
		v.remove(0);
		System.out.println(v);
	}

}

輸出:

before calling public abstract boolean java.util.List.add(java.lang.Object)
hello
before calling public abstract boolean java.util.List.add(java.lang.Object)
world
before calling public java.lang.String java.lang.Object.toString()
[hello, world]
before calling public abstract java.lang.Object java.util.List.remove(int)
0
before calling public java.lang.String java.lang.Object.toString()
[world]

能夠看到與Vector操做相關的默認行爲已經發生了變化,這是因爲咱們施加了相關的代理。

程序實踐2:代理多個類的實例

package com.test.dynamicproxy;

public interface Foo
{
    void doAction();
}




package com.test.dynamicproxy;

public class FooImpl implements Foo
{
    public FooImpl()
    {
    }

    public void doAction()
    {
        System.out.println("in FooImp1.doAction()");
    }
}




package com.test.dynamicproxy;

public class FooImpl2 implements Foo
{
    public FooImpl2()
    {
    }

    public void doAction()
    {
        System.out.println("in FooImp2.doAction()");
    }

}



package com.test.dynamicproxy;

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

public class CommonInvocationHandler implements InvocationHandler
{

    // 動態執行對象,須要回調的對象
    private Object target;

    // 支持構造方法注射
    public CommonInvocationHandler()
    {

    }

    // 支持構造方法注射
    public CommonInvocationHandler(Object target)
    {
        setTarget(target);
    }

    /**
     * 
     * 採用setter方法注射
     * 
     * @param target
     * 
     */
    public void setTarget(Object target)
    {
        this.target = target;
    }

    /**
     * 
     * 調用proxy中指定的方法method,並傳入參數列表args
     * 
     * @param proxy
     *            代理類的類型,例如定義對應method的代理接口
     * 
     * @param method
     *            被代理的方法
     * 
     * @param args
     *            調用被代理方法的參數
     * 
     * @return
     * 
     * @throws java.lang.Throwable
     * 
     */

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        return method.invoke(target, args);
    }

}





package com.test.dynamicproxy;

import java.lang.reflect.Proxy;

public class Demo
{
	public static void main(String[] args)
	{

		// 1.通用的動態代理實現

		CommonInvocationHandler handler = new CommonInvocationHandler();

		Foo f;

		// 2.接口實現1

		handler.setTarget(new FooImpl());

		// 方法參數說明:代理類、代理類實現的接口列表、代理類的處理器

		// 關聯代理類、代理類中接口方法、處理器,但代理類中接口方法被調用時,會自動分發處處理器的invoke方法

		// 若是代理類沒有實現指定接口列表,會拋出非法參數異常

		f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),

		new Class[] { Foo.class },

		handler);

		f.doAction();

		// 3.接口實現2

		handler.setTarget(new FooImpl2());


		f.doAction();
	}
}
相關文章
相關標籤/搜索