理解代理模式,最好先了解反射原理–反射
为什么要使用代理类?
通俗来讲,代理类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);
}
}