其实每个模式名称就表明了该模式的作用,代理模式就是多一个代理类出来,替源对象进行一些操作,比如我们在租房子的时候会去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。
代理(proxy)模式:指目标对象给定代理对象,并由代理对象代替真实对象控制客户端对真实对象的访问。
代理模式模式有以下角色:
抽象主题(subject)角色:声明真实主题和代理主题的共同接口。
真实主题(real subject)角色:定义代理对象需要代理的真实对象。
代理主题(proxy subject)角色:代替真实对象来控制对真实对象的访问,代理对象持有真实对象的应用,从而可以随时控制客户端对真实对象的访问。
代理模式结构类图:
代理模式在java里面很常见,在开源框架里如spring,mybatis等里面大量使用。现实生活中也很常见,比如我们访问facebook主站,常会选择一些代理,通过代理访问facebook。代理分静态代理和动态代理,java对动态代理有很好的支持,提供了InvocationHandler接口和Proxy类。
java API 对InvocationHandler接口和Proxy类的介绍:
Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。创建某一接口 Foo 的代理:
InvocationHandler handler = new MyInvocationHandler(...);
Class proxyClass = Proxy.getProxyClass(
Foo.class.getClassLoader(), new Class[] { Foo.class });
Foo f = (Foo) proxyClass.
getConstructor(new Class[] { InvocationHandler.class }).
newInstance(new Object[] { handler });
或使用以下更简单的方法:
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class[] { Foo.class },
handler);
动态代理类(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。
代理接口是代理类实现的一个接口。
代理实例是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序对象,它可以实现接口 InvocationHandler。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的 Invoke 方法,并传递代理实例、识别调用方法的 java.lang.reflect.Method 对象以及包含参数的 Object 类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。InvocationHandler 是代理实例的调用处理程序实现的接口。每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。
这里举个好理解的例子:公司项目部需要CEO签署一个文件,项目负责人会把文件交给CEO助理,助理会收文件,等到CEO回来后递CEO,CEO签署后交给助理,助理收好交给项目负责人。这个过程中项目负责人其实不知道是否真的是CEO签署的文件,有可能是助理打印的CEO的签名到文件上。这样助理就是一个代理角色,代替CEO处理事务。静态代理类图如下:
代码如下:
1.静态代理:
抽象主题角色:
public interface Leader {
public abstract void sign();
}
真实主题角色:
public class CEO implements Leader{
@Override
public void sign() {
// TODO Auto-generated method stub
System.out.println("CEO签文件");
}
}
代理主题角色:
public class Assistant implements Leader {
private Leader leader;
public Assistant(Leader leader) {
super();
this.leader = leader;
}
@Override
public void sign() {
// TODO Auto-generated method stub
System.out.println("递给领导");
leader.sign();
System.out.println("装入袋子,送出");
}
}
2.动态代理:
public class AssistantHandler implements InvocationHandler {
// 目标对象
private Object targetObject;
private CEO ceo;
public AssistantHandler(CEO ceo) {
// TODO Auto-generated constructor stub
this.ceo=ceo;
}
// 描述:创建代理对象 这段也可以不在此类,也可以放在客户端里面
public Object createProxy(Object targetOjbect) {
this.targetObject = targetOjbect;
return Proxy.newProxyInstance(targetOjbect.getClass().getClassLoader(), targetOjbect.getClass().getInterfaces(),
this);
};
/**
* 此方法为必须实现的,在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
Object result = null;
System.out.println("递给领导");
result = method.invoke(this.targetObject, args);
System.out.println("装入袋子,送出");
return result;
}
}
测试类:
public class Mai {
public static void main(String[] args) {
// 静态代理测试
CEO ceo = new CEO();
Leader leader1 = new Assistant(ceo);
leader1.sign();
System.out.println("=========================================");
// 动态代理测试,一些三种方式都可以获得动态代理对象
AssistantHandler ah = new AssistantHandler(ceo);
Leader leader2 = (Leader) ah.createProxy(new CEO());
leader2.sign();
// Leader leader3 = (Leader)
// Proxy.newProxyInstance(CEO.class.getClassLoader(),
// ceo.getClass().getInterfaces(), ah);
// leader3.sign();
/*Leader leader4 = (Leader) Proxy.newProxyInstance(Leader.class.getClassLoader(), new Class[] { Leader.class },
ah);
leader4.sign();*/
}
}
结果如图:
通过上面例子和代码可以看出,动态代理显得更为灵活,实际过程中动态代理也较为常用。
代理模式的一些应用场景:
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有俩种方法:
1.修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
2.就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式
优点:
使用代理模式,可以将功能划分的更加清晰,有助于后期维护~