概述
正文开始之前我们先考虑一个问题:什么叫做 代理(Proxy)
?
按照 维基百科
定义:
代理(英语:Proxy)也称网络代理,是一种特殊的网络服务,允许一个网络终端(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接。一些网关、路由器等网络设备具备网络代理功能。一般认为代理服务有利于保障网络终端的隐私或安全,防止攻击。
通俗讲 代理
就类似一个连接在客户端和服务器之间的桥梁,连通客户端和服务器之间的请求和响应,代理的存在一方面可以保护服务器的安全,在代理部分可以对请求信息进行过滤,隔绝一部分非法的请求信息吗,另一方面可以提高用户的访问速度,其具体功能可以借助下边的图来帮助理解。
如果你理解上述 代理
的概念,那么 代理设计模式
也就不难理解了。代理设计模式
就是对上边上 客户端-代理-服务器
三者链式关系的一种抽象,进而应用到软件开发中的一种通用设计模式。
代理设计模式有如下三个优点:
- 保护真实对象
- 让对象职责更加明确
- 易于扩展
在 java 开发中代理设计模式有三种实现方法:
- 静态代理
- 动态代理 jdk 实现
- 动态代理 cglib 实现
下边我们分三种情况对这三种代理设计模式的实现进行讨论和分析
静态代理
UML 类图
KeHu
:客户端MiShhu
:中介LaoZong
:服务器GongNeng
:服务器和中介要同时实现的功能接口
代码实现
GongNeng
的 java 代码:
/**
* @program: TestBlog
* @description:
秘书和老总都要实现的功能接口
* @author: vcjmhg
* @create: 2019-10-07 16:31
**/
public interface GongNeng {
public void ZuoShengYi();
public void eat();
}
Kehu
的 java 代码
/**
* @program: TestBlog
* @description:
客户相当于客服端
* @author: vcjmhg
* @create: 2019-10-07 16:31
**/
public class KeHu {
public static void main(String[] args) {
MiShu miShu=new MiShu();
miShu.ZuoShengYi();
}
}
MiShu
的 java 代码
/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 16:31
**/
public class MiShu implements GongNeng{
private LaoZong laoZong=new LaoZong();
public void ZuoShengYi() {
System.out.println("秘书:请问您预约来吗?");
laoZong.ZuoShengYi();
System.out.println("秘书备注访客信息");
}
public void eat() {
System.out.println("秘书:请问您预约来吗?");
laoZong.eat();
System.out.println("秘书备注访客信息");
}
}
LaoZong
的 java 代码:
package proxy.staticproxy;
/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 16:31
**/
public class LaoZong implements GongNeng{
public void ZuoShengYi() {
System.out.println("老总:谈个小项目!!");
}
public void eat() {
System.out.println("老总:吃饭!!");
}
}
运行结果为:
秘书:请问您预约来吗?
老总:谈个小项目!!
秘书备注访客信息
Process finished with exit code 0
代码地址
详细的代码可以参看 github 的上的代码
静态代理的不足
毫无疑问静态代理作为最容易实现或者说最直观的的代理设计模式的实现方式,代理模式具有的优点它必然也具有,但另一方面它也有许多缺点:
- 代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
- 代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。
为了解决该问题我们引入了动态代理
动态代理之 jdk 实现
UML 类图
Client
:客户端MiShhu
:中介LaoZong
:服务器GongNeng
:服务器和中介要同时实现的功能接口
代码实现
Client
的 java 代码
/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 17:04
**/
public class Client {
public static void main(String[] args) {
//第一个参数:反射时使用的类加载器
//第二个参数:Proxy需要实现什么接口
//第三个参数:通过接口对象调用方法时,需要调用哪个类的invoke方法
GongNeng gongneng = (GongNeng) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{GongNeng.class}, new MiShu());
gongneng.eat();
}
}
GongNeng
的 java 代码:
public interface GongNeng {
public void ZuoShengYi();
public void eat();
}
MiShu
的 java 代码
/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 16:56
**/
public class MiShu implements InvocationHandler {
private LaoZong laozong=new LaoZong() ;
//代理类针对被代理对象类似的功能不需要重复实现多次
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("预约时间");
Object result = method.invoke(laozong, args);
System.out.println("记录访客信息");
return result;
}
}
LaoZong
的 java 代码:
/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 16:49
**/
public class LaoZong implements GongNeng{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void ZuoShengYi() {
System.out.println("老总:谈生意");
}
public void eat() {
System.out.println("老总:吃饭!!");
}
}
运行结果为:
预约时间
老总:吃饭!!
记录访客信息
Process finished with exit code 0
利用 JDK 实现动态代理的优点
相比与静态代理,利用 JDK 实现动态代理的方式实现了代理类和功能接口之间的解耦。对于委托类如果增加某个方法,对于代理类代码几乎可以不变,减少了代码的复杂性,使其更加易于维护。另一方面在代理不同类型对象时可以实现代码一定程度的复用。
利用 JDK 实现动态代理的不足
但是该方法实现动态代理也有一定不足,由于其内部借助反射实现代理设计模式,系统开销大效率低。而且其委托类仍需实现功能接口,代码耦合性还是不够低。
代码地址
详细的代码可以参看 github 的上的代码
动态代理之 cglib 实现
UML 类图
Client
:客户端MiShhu
:中介LaoZong
:服务器
代码实现
Client
的 java 代码
public class Client {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(LaoZong.class);
enhancer.setCallback(new MiShu());
LaoZong laozong = (LaoZong) enhancer.create();
laozong.chifan();
}
}
MiShu
的 java 代码:
public class MiShu implements MethodInterceptor{
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
System.out.println("预约时间");
Object result = arg3.invokeSuper(arg0, arg2);
System.out.println("备注");
return result;
}
}
LaoZong
的 java 代码:
public class LaoZong {
public void chifan() {
System.out.println("吃饭");
}
public void mubiao() {
System.out.println("目标");
}
运行结果为:
预约时间
吃饭
备注
Process finished with exit code 0
利用 cglib 实现动态代理的优点
通过 cglib 方式几乎完美的解决来 jdk 方式所具有的缺点一方面 cglib 方式内部是通过字节码方式实现动态代理,效率高,执行速度快;另一方面,该方式解耦了委托类和功能接口之间的耦合,提高了代码的灵活性。
代码地址
详细的代码可以参看 github 的上的代码
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于