现在的位置: 首页 > 综合 > 正文

设计模式——29、代理模式(proxy)

2017年11月30日 ⁄ 综合 ⁄ 共 5442字 ⁄ 字号 评论关闭

代理模式的作用:为其他对象提供一种代理以控制对这个对象的访问。

在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

1、代理模式一般涉及到的角色:

      a)抽象角色:声明真实对象和代理对象的共同接口

      b)代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装

      c)真实角色:代理角色所代表的真实对象,是我们最终要引用的对象

代码举例:

抽象角色:

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

真实角色:

public class RealSubject extends Subject
{

	public void request()
	{
		System.out.println("real subject");
	}

}

代理角色:

public class ProxySubject extends Subject
{
	private RealSubject realSubject;//代理对象角色内部含有的对真实对象的引用

	public void request()
	{
		this.preRequest();//在真实角色操作完成前的附加操作
		if(null == realSubject)
		{
			realSubject = new RealSubject();
		}
		
		realSubject.request(); //真实角色所完成的事情
		
		this.postRequest();//在真实角色操作完成后的附加操作
	}
	
	private void preRequest()
	{
		System.out.println("pre proxy");
	}
	
	private void postRequest()
	{
		System.out.println("post proxy");
	}

}

客户端测试:

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

 客户实际需要调用的是RealSubject类的request()方法,现在用ProxySubject来代理RealSubject类,同样达到目的,同时还封装了其他方法(preRequest(),postRequest()),可以处理一些其他问题。另外,如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决(上例是静态代理模式)。

2、动态代理

Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
    (1)Interface InvocationHandler:该接口中仅定义了一个方法:public object invoke(Object obj,Method method, Object[] args)
    在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。
    (2)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接口中声明过的方法)

所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。在使用动态代理类时,我们必须实现InvocationHandler接口

动态代理的抽象角色:

public interface Subject
{
	public void request();
}

动态代理的真实角色:

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

}

动态代理角色:

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

/**
 * 该代理类的内部属性是Object类型,实际使用时是通过该类的构造方法传递进来的一个对象
 * 此外,该类还实现了invoke方法,该方法中的method.invoke其实就是调用被代理对象的将要
 * 执行的方法,方法的参数是sub,表示该方法从属于sub对象,通过动态代理类,我们可以在执行真实对象的
 * 方法前后加入自己的一些额外方法。
 */
public class DynamicSubject implements InvocationHandler
{
	private Object sub;
	
	public DynamicSubject(Object obj)
	{
		this.sub = obj;
	}
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable
	{
		System.out.println("before:" + method);
		
		method.invoke(sub, args);//等价于上例中ProxySubject的realSubject.request();
		
		System.out.println("after:" + method);
		
		return null;
	}

}

客户端使用测试:

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

public class Client
{
	public static void main(String[] args)
	{
		RealSubject realSubject = new RealSubject();
		
		InvocationHandler handler = new DynamicSubject(realSubject);
		
		Class<?> classType = handler.getClass();
		//
		
		Subject subject = (Subject)Proxy.newProxyInstance(classType.getClassLoader(),
				realSubject.getClass().getInterfaces(),handler);
		
		subject.request();
	}
}

这里要注意几个关注点:

      1)关于DynamicSubject,它含有一个对真实对象的引用,并通过构造方法进行真实对象的传递,然后他又实现了InvocationHandler接口,重写了invoke方法,这个invoke方法的功能就是使用反射的机制调用真实对象的方法。对比一下第一个例子的ProxySubject类,这是静态代理类,它的功能也是持有一个真实对象的引用,然后request方法调用真实对象的方法,这两个类的功能相似。

      2)关于Client类中的Subject subject = (Subject)Proxy.newProxyInstance(classType.getClassLoader(),realSubject.getClass().getInterfaces(),handler);

newProxyInstance()方法生成一个类实例,这个实例实现了realSubject所实现的接口,也就是抽象角色,而且传递了一个hangdler,也就是DynamicSubject,这条语句就是来生成一个真正地代理类,因为他不是提前就写好的,而是在运行时生成的(它的类名是$Proxy+n,n从0开始,这里就是$Proxy0),这个类才是我们真正意义上说的代理类,只是代理类的功能都转交给类DynamicSubject来实现,就好像是$Proxy0又封装了handler。我们调用代理类的方法:subject.request()相当于$Proxy0.request(),相当于$Proxy0.handle.invoke(),

      3)InvocationHandler的invoke方法,就是使用反射的机制调用真实对象的一种通用方法,它的method参数就是客户端要调用的抽象角色的方法,这里就是Subject的request方法,Object[] args是方法需要的参数。所以说DynamicSuject是实现代理类通用功能的一个“功能包装类”,对于给定的不同真实角色,他就完成对应的真实角色功能,而Proxy的newProxyInstance方法则实现对DynamicSubject的包装(这时的DynamicSubject是实现了特定真实角色功能的确定类:RealSubject
realSubject = new RealSubject();         InvocationHandler handler = new DynamicSubject(realSubject);这两句就是DynamicSubject与特定真实角色的绑定
),运行时形成真正地代理类对象,由于代理类是运行时动态生成的,就减少了人工手写代理类的数量,同时,对于给定的真实角色,要相应动态生成代理类,所以运行时的类数量没有减少,依然是一个代理类对应一个真实角色类。


3、动态代理步骤

    1).创建一个实现接口InvocationHandler的类,它必须实现invoke方法
    2).创建被代理的类以及接口
    3).通过Proxy的静态方法   newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理
    4).通过代理调用方法

4、动态代理举例

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)
	{
		this.proxyObj = obj;
	}
	
	public static Object factory(Object obj)
	{
		Class<?> classType = obj.getClass();
		
		return Proxy.newProxyInstance(classType.getClassLoader(),
				classType.getInterfaces(), new VectorProxy(obj));
		
	}
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable
	{
		System.out.println("before:" + method);
		
		if(null != args)
		{
			for(Object obj : args)
			{
				System.out.println(obj);
			}
		}
		
		Object object = method.invoke(proxyObj, args);
		
		System.out.println("after:" + method);
		
		return object;
		
	}
	
	public static void main(String[] args)
	{
		List v = (List)factory(new Vector());
		
		System.out.println(v.getClass().getName());
		
		v.add("new");
		v.add("york");
		
	}

}

 

抱歉!评论已关闭.