JAVA进阶-代理类Proxy详述

理解代理模式,最好先了解反射原理–反射

为什么要使用代理类?

通俗来讲,代理类P是将一个类A进行包装保护,以控制对这个类A的访问。

在某些情况下,一个客户不想或者不能直接引用另一个类A的实例对象a,我们就可以通过将a交给代理类P进行包装,将代理类P返回给客户,这样一来,客户可以通过对代理类P的操作,替代对a的的操作,我们则可以通过设置代理类P进行对a进行访问控制:

如果允许,那么代理类P将会把方法以及参数传递给a,a执行后将结果传回P,P再将结果返回给用户。
如果不允许,我们可以在代理类中进行相应设置。

在这里插入图片描述
 
 
 

代理类P对A的封装

要实现代理类P对A的封装,首先要知道什么是代理类。
可以这么认为,实现了 InvocationHandler 接口的类就是代理类。
 
InvocationHandler 接口内部定义了一个invoke方法:

 Object invoke(
 				Object proxy 	封装了A的代理类P,
 				 Method method  通过P调用的A的方法, 
 				 Object[] args  方法参数		)

当客户有了一个封装了a的代理类p时,可将对a的操作直接转换为对p(的操作。

比如:客户想进行a.compareTo(b) 操作,则可直接使用 p.compareTo(b)

代理类p会将compareTo方法传递给invoke方法,在invoke方法内对客户是否可以调用compareTo方法进行处理

如果允许调用,就通过反射机制调用a.compareTo(b),并将结果返回给客户。

class P implements InvocationHandler{ //代理

	private Object target;
	
	public P(Object a){
		target = a;
	}
	
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// TODO Auto-generated method stub
		if(method.getName().equals("hashCode")){
			System.out.println("非法访问!!");
			return 0 ;
		}
		return method.invoke(target, args);
	}

上方代理类P实现了** InvocationHandler **接口,私有成员target用于保存封装的对象a,在invoke方法中对访问进行控制,如果客户想调用a.hashCode()方法,就输出非法访问并返回0。

如果客户调用其他方法,则允许访问。
 
 
 
 

实例分析

现在我们构造一个Interger类型的数组,其中升序存储0-999,通过一个简单的二分查找的例子来讲解代理类的作用。

		Object[] elements = new Object[1000]; 		    构造一个用于二分查找的数组
		for (int i = 0; i < elements.length; i++) {
			Integer value = i;
			elements[i] = value;
		}

那么如果我们将elements数组直接交予客户,客户就可以对Interger对象进行任意操作。

如果我们的Interger对象非常害羞,我们不希望客户调用Interger对象的hashCode()方法。
 
 
于是我们将Interger对象交由上述的代理类P进行封装,代码就变成下面的样子:

		Object[] elements = new Object[1000];
		for (int i = 0; i < elements.length; i++) {
			Integer value = i;
			InvocationHandler p = new P(value);    //代理类封装Integer(value)
			Object proxy = Proxy.newProxyInstance(null,new Class[]{Comparable.class}, p);
										  得到代理实例
			elements[i] = proxy;
		}

 

你可能会对这句代码产生疑问

Object proxy = Proxy.newProxyInstance(null,new Class[]{Comparable.class}, handler);

为什么不将 elements[i] 直接用 p 赋值,而是得到代理实例proxy后才能赋值给elements[i]

不能用
elements[i] = proxy;
替换
Object proxy = Proxy.newProxyInstance(null,new Class[]{Comparable.class}, p);
elements[i] = proxy;

显而易见,p只是一个代理类,它的定义清清楚楚的写在上面,我们这里将它的定义再拿过来

class P implements InvocationHandler{ //  代理类P的定义
 
	private Object target;
	
	public P(Object a){
		target = a;
	}
	
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// TODO Auto-generated method stub
		if(method.getName().equals("hashCode")){
			System.out.println("非法访问!!");
			return 0 ;
		}
		return method.invoke(target, args);
	}

p.compareTo并不会将compareTo作为参数传递给invoke方法,而是会在类P中寻找compareTo方法的定义。

Proxy.newProxyInstance(null,new Class[]{Comparable.class}, p);会通过p构造一个代理实例proxy。

proxy.compareTo会将compareTo作为参数传递给invoke方法。
 

通过Proxy类的静态方法newProxyInstance返回一个接口的代理实例。针对不同的代理类,传入相应的代理程序控制器InvocationHandler。

 

现在我们已经构造了一个代理类proxy的对象数组,proxy中封装着我们要保护的类Integer。

为了效果更明显,我们在invoke方法中增加一些输出用以显示调用了哪些方法

class P implements InvocationHandler{ //代理

	private Object target;
	
	public P(Object a){
		target = a;
	}
	
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// TODO Auto-generated method stub

		if(method.getName().equals("hashCode")){
			System.out.println("非法访问!!");
			return 0 ;
		}
		
		//用于显示调用了的方法↓↓
		System.out.print(target+"."+method.getName()+"("); 
		if (args!=null) {		//输出参数
			
			for (int i = 0; i < args.length; i++) {
				System.out.print(args[i]);
				if (i>0) {
					System.out.print(",");
				}
			}
			
		}
		System.out.println(")");
		//用于显示调用了的方法↑↑



		return method.invoke(target, args);
	}

我们调用

Arrays.binarySearch(elements, 357);     会调用数组内元素的compareTo方法

可得到输出
在这里插入图片描述
可以看到,调用Proxy.compareTo()方法时,代理实例Proxy会帮我们调用Integer.compareTo()
 
 
 

如果调用hashcode()方法

//		Arrays.binarySearch(elements, 357);
		elements[50].hashCode()	;

结果如下:

在这里插入图片描述
可见,通过代理类p实现了对Integer类的控制访问。

 
 
 

完整代码

完整代码如下

package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
 * 代理类P包装了一个类A,A实现了F接口
 * 通过Proxy.newProxyInstance方法,传入接口F,包装类A的代理P 生成了一个代理实例Proxy
 * 这样一来,原来调用A的方法 A.method 可以替换为 Proxy.method
 * Proxy内部会自动调用重写过的 invoke方法,将method作为参数传给invoke
 * @author Pinker_Q
 *
 */
public class ProxyTest {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Object[] elements = new Object[1000];
		for (int i = 0; i < elements.length; i++) {
			Integer value = i;
			InvocationHandler p = new P(value);
			Object proxy = Proxy.newProxyInstance(null,new Class[]{Comparable.class}, p);
			elements[i] = proxy;
		}
	
		Arrays.binarySearch(elements, 357);
//		elements[50].hashCode()	;
		
	}

}


class P implements InvocationHandler{ //代理类
	private Object target;
	public P(Object a){ 			//构造方法
		target = a;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
	{
		if(method.getName().equals("hashCode")){
			System.out.println("非法访问!!");
			return 0 ;
		}
		
		//用于显示调用了的方法↓↓
		System.out.print(target+"."+method.getName()+"("); 
		if (args!=null) {		//输出参数
			
			for (int i = 0; i < args.length; i++) {
				System.out.print(args[i]);
				if (i>0) {
					System.out.print(",");
				}
			}
			
		}
		System.out.println(")");
		//用于显示调用了的方法↑↑
		

		return method.invoke(target, args);
	}
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值