代理模式为一个对象提供一个替身或占位符以控制对这个对象的访问。
通俗点说就是通过使用代理对象来控制对真实对象访问的控制。
假设存在一个A对象,他有一个方法叫helloworld();
我们想要控制只有部分人才能调用helloworld方法,这是我们采用代理模式,为A对象创建一个代理对象(ProxyA)
使用者不直接调用A的方法,而是调用ProxyA的方法,并在ProxyA中对使用者的身份进行判断,符合要求则将调用请求传递给真实对象 A。
Java在java.lang.reflect包中有自己的代理支持,利用这个包可以在运行时动态地创建一个代理类,实现一个或多个接口,并将方法的调用转发到指定的类。因为实际的代理类是在运行时创建的,我们称这个java技术为:动态代理。
动态代理使用示例:
1、 前提:还没有需要控制对真实对象的访问,有两个动作ActionOne和ActionTwo;
//第一种动作,包含sayHello方法
public interface ActionOne {
public void sayHello(String word);
}
//实现第一种动作
public class ActionOneImpl implements ActionOne {
@Override
public void sayHello(String word) {
// TODO Auto-generated method stub
System.out.println("hello "+word);
}
}
//第二种动作,包含introduce方法
public interface ActionTwo {
public void introduce(String name);
}
//实现第二种动作
public class ActionTwoImpl implements ActionTwo {
@Override
public void introduce(String name) {
// TODO Auto-generated method stub
System.out.println("hello,i am "+name);
}
}
2、 实现测试类:
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
ActionOne actionOne = new ActionOneImpl();
actionOne.sayHello("world!");
ActionTwo actionTwo = new ActionTwoImpl();
actionTwo.introduce("markey");
}
}
运行结果:
3、 引入新需求,需要在每个动作执行前做某个动作(例如打印“Begin your show,please!”)
4、 假设此时ActionOne和ActionTwo已经相当复杂了,我们不能轻易改变其实现,那么我们可以有几种思路:
- 采用装饰器模式,在Action类外面再包一层;(此处不做说明)
- 采用代理模式,动态创建代理对象;
- ……
5、 使用代理模式,关键有两步:实现InvocationHandle接口,和创建代理对象
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//代理处理器,调用代理对象的操作都会转发到这里,再转发给真实对象。所以在此处进行访问控制,例如每次调用前打印点什么。
public class LimitsInvocationHandle implements InvocationHandler {
Object target; //真实对象,即代理的对象
//需要将真实对象传递进行,与代理处理器进行绑定
public LimitsInvocationHandle(Object target) {
super();
this.target = target;
}
@Override
/*
* (non-Javadoc)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
* 第一个参数:proxy是代理对象,
* 第二个参数:当前调用那个方法,
* 第三个参数:方法的参数。
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
System.out.println("Begin you show, please"); //在真实对象的方法调用前做点什么
method.invoke(target, args); //将方法调用转发给真实对象
return null;
}
}
6、 动态创建代理对象,调用代理对象来执行方法
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
ActionOne actionOne = new ActionOneImpl(); //创建真实对象
ActionOne actionOneProxy = (ActionOne) getProxy(actionOne); //动态创建代理对象
actionOneProxy.sayHello("world"); //调用代理对象
ActionTwo actionTwo = new ActionTwoImpl();
ActionTwo actionTwoProxy = (ActionTwo) getProxy(actionTwo);
actionTwoProxy.introduce("Markey");
actionOneProxy.sayHello("world,again");//再次调用代理对象
}
//创建代理对象方法
public static Object getProxy(Object action) {
/*
* 第一个参数是类加载器,
* 第二个参数是真实委托对象所实现的的接口,
* 第三个参数是代理处理器,其中实现了访问控制
*/
return Proxy.newProxyInstance(action.getClass().getClassLoader(),
action.getClass().getInterfaces(), new LimitsInvocationHandle(action));
}
}
运行结果
动态代理重点在于实现代理控制类(InvocationHandle),用于控制对于真实对象的访问。
真实对象的代码不需改变,在调用方法前采用java自带的Proxy对象来动态创建代理对象(以真实对象和代理控制类为参数)。创建动态代理对象的过程可灵活运用其他设计模式。
完整示例代码:
https://github.com/markey92/helloworld/tree/master/src/com/markey/proxy