代理模式,代理模式可以说我们所有的面向对象领域用的非常广泛的一种设计模式了。也解决了很多的实际问题。在生活中,我们也能够经常看到这种代理。
什么是代理?
定义:就是控制对某个对象的访问
说通俗一点代理就是相当于我们要去买房子找的房屋中介一样,代理就是房屋中介。而这个房屋中介的存在就是为了防止买房的人和房东之间直接联系(这一点很好的解释了代理的定义)。
从上面这个例子中我们抽象出买房的人、房屋中介和房东。房屋中介就是处于买房的人和房东之间的一个沟通桥梁(官方是这样说的,代理类持有被代理类的接口)。所以,房屋中介既要和买房的人有关系,也要和房东有关系。 所以,代理模式一般基本上由抽象接口、代理类和真实类组成。其代理类和真实类要实现抽象接口。代理类就好比房屋中介,真实类就是房东。而这个抽象接口就好比客户和房屋中介以及房东共同约定好的一组规则。只要按照规则办事,才有可能达成协议。
代理模式应用场景:
- -安全代理:屏蔽对真实角色的直接访问
- -远程代理:通过代理类处理远程方法调用(RMI)
- -延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
代理模式可以分为静态代理和动态代理:
- 静态代理:就是代理类我们自己写的。
上面的例子太俗气,但是往往俗气的东西容易理解。下面我给出官方的定义,大家结合着来看:代理设计模式的核心角色只有一下: - 抽象角色:
-定义代理角色和真实角色的公共对外方法(一般用接口来实现) - 真实角色:
-实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用
-关注真正的业务逻辑 - 代理角色:
-实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现
抽象方法,并可以附加自己的操作。
-将统一的流程控制放到代理角色中处理。
下面我们就以买房子为例,就用java代理来进行实现:
首先我们给出抽象角色:
public interface PublicInterface {
//看房子
void kanfangzi();
//谈价钱
void tanjiaqian();
//签协议
void qianxieyi();
//拎包入住
void ruzhu();
}
我们再给出真实角色(实现抽象接口):
public class FangDong implements PublicInterface {
@Override
public void kanfangzi() {
// TODO Auto-generated method stub
System.out.println("房东带领中介和客户看房子");
}
@Override
public void tanjiaqian() {
// TODO Auto-generated method stub
System.out.println("房东和客户谈价钱");
}
@Override
public void qianxieyi() {
// TODO Auto-generated method stub
System.out.println("房东和客户签订三方协议");
}
@Override
public void jiaoyaoshi() {
// TODO Auto-generated method stub
System.out.println("房东交出房门钥匙");
}
}
给出静态代理类(实现抽象接口):
public class Proxy implements PublicInterface{
//代理类要持有真实类的接口
private PublicInterface publicInterface;
public Proxy(PublicInterface publicInterface) {
// TODO Auto-generated constructor stub
this.publicInterface = publicInterface;
}
@Override
public void kanfangzi() {
// TODO Auto-generated method stub
System.out.println("房屋中介和客户看房子");
}
@Override
public void tanjiaqian() {
// TODO Auto-generated method stub
System.out.println("房屋中介一起谈价钱");
}
@Override
public void qianxieyi() {
// TODO Auto-generated method stub
System.out.println("房屋中介签三方协议");
}
@Override
public void jiaoyaoshi() {
// TODO Auto-generated method stub
//房屋中介找房东交出钥匙给客户
publicInterface.jiaoyaoshi();
}
}
最后,我们写个main方法测试一下,看看是否正确:
public class Test {
public static void main(String []args) {
//创建一个真实的对象
PublicInterface i = new FangDong();
//创建一个代理对象,把需要代理的对象作为参数传入到代理对象中
Proxy proxy = new Proxy(i);
//看房子
proxy.kanfangzi();
//谈价钱
proxy.tanjiaqian();
//签协议
proxy.qianxieyi();
//交出钥匙
proxy.jiaoyaoshi();
}
测试结果:
看以看到,前面三个方法都是房屋中介干的,而交钥匙这个方法是由房东干的事情。准确来说,应该是房屋中介通知房东干的。不可能是房屋中介交出钥匙,因为他没有钥匙,整个过程中,房屋中介就是一个桥梁(代理)。
- 动态代理:代理类是不需要我们自己写,java帮我们动态生成。
我们再来了解一下动态代理,上面也已经说过,代理代理和静态代理不同的地方就在于代理类。静态代理类是由程序员自己来写,而动态代理不需要程序员自己来写,由java本身动态生成。还有一点就是动态代理是java目前还在用的。静态代理并不常用。因为要做到统一,普遍,面向接口。那静态的肯定不行了。这就是静态不常用的原因之一了。好了,长话短说,我们用代码来实现一下:
抽象接口还是要有的:
public interface PublicInterface {
//看房子
void kanfangzi();
//谈价钱
void tanjiaqian();
//签协议
void qianxieyi();
//房东交出钥匙
void jiaoyaoshi();
}
真实角色也还是要有的:
public class FangDong implements PublicInterface {
@Override
public void kanfangzi() {
// TODO Auto-generated method stub
System.out.println("房东带领中介和客户看房子");
}
@Override
public void tanjiaqian() {
// TODO Auto-generated method stub
System.out.println("房东和客户谈价钱");
}
@Override
public void qianxieyi() {
// TODO Auto-generated method stub
System.out.println("房东和客户签订三方协议");
}
@Override
public void jiaoyaoshi() {
// TODO Auto-generated method stub
System.out.println("房东交出房门钥匙");
}
}
接下来就是动态生成代理对象,这里补充一下,动态代理本文中采用javaJDK自带来实现。
public class HandleDanimicProxy implements InvocationHandler {
//代理类要持有真实类的接口
private PublicInterface publicInterface;
public HandleDanimicProxy(PublicInterface publicInterface) {
// TODO Auto-generated constructor stub
this.publicInterface = publicInterface;
}
//调用方法前:方法前置处理
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
//如果调用的方法没有返回值
method.invoke(publicInterface, args);
//如果调用的方法有返回值,return o:
//Object o = method.invoke(publicInterface, args);
if(method.getName().equals("jiaoyaoshi")) {
System.out.println("这里是一个方法的过滤,如果调用的方法是交钥匙的方法,就做出一些操作");
}
//调用方法后:方法后置处理
return null;
}
}
这里在来解释一下上面的部分代码。我们说静态代理和动态代理的区别就在与代理类的创建。当然不止这一点。后面我还会再说。上面这个类主要是实现了一个处理器接口(即InvocationHandler这个接口),这个接口会要求我们重写Invoke()方法,我们在主方法中调用的所有方法都会从Invoke方法的内部穿过(这也是动态代理的优点,方便做到流程控制)。解释一下invoke()方法中的参数:
参数一:就是需要代理的类
参数二:需要代理的类中的一些方法
参数三:就是如果看我们调用代理的类的方法时有参数,可以传入参数。就是真实角色方法中的参数。
好,我i们写一个主方法来测试一下:
public class DanimicTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//1.创建一个真实角色
PublicInterface i = new FangDong();
//2.生成处理器对象
HandleDanimicProxy handle = new HandleDanimicProxy(i);
//3.生成代理对象
PublicInterface proxy = (PublicInterface) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] {PublicInterface.class},handle);
proxy.kanfangzi();
proxy.tanjiaqian();
proxy.qianxieyi();
proxy.jiaoyaoshi();
}
}
主方法中步骤1,步骤2,步骤3没有什么好解释的。都是一些套路,如果你实在看不懂,就先记下这样写。等你学的东西多了,或许有一天回过头就看明白了。这里只说明一点,就是在第3步动态生成代理对象的时候,里面有三个参数:
参数一:调用系统的类加载器加载就可以
参数二:就是我们写的抽象接口,这里是得到接口的反射入口
参数三:就是我们步骤2中创建的处理器接口
动态生成代理类的方法:
- 通过java自带的JDK来实现
- 通过javaasisite字节码操作实现
- 通过CGLIB 实现
- ASM(底层使用指令,可维护性较差)
动态代理相比于静态代理的优点
- 抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的 方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。因为所有被调用的方法都要经过Invoke()方法的内部。
最后,总结一下。因为本人的英语水平实在不敢恭维。所以代码中有很多地方命名不规范。也顺便提醒大家,要想在这个领域有一番成就,英语和数学都要有学好。
总结如下:
静态代理:
静态代理的一般步骤:
- 创建抽象接口。
- 创建真实角色并实现抽象接口。
- 创建代理对象并实现抽象接口。
动态代理一般步骤:
- 创建抽象接口。
- 创建真实角色。
- 创建处理器接口(前提我们得实现核心处理器接口,重写invoke方法)。
- 动态构建代理对象。
备注:
5. JDK自带的动态代理
-java.lang.reflect.Proxy
* 作用:动态生成代理类和对象
-java.lang.reflect.InvocationHandler(处理器接口)
* 可以通过invoke方法实现对真实角色的代理访问
* 每次通过Proxy生成代理类对象时都要指定对应的处理器对象
代理模式到这里就分享完了,再见……