Java静态代理与动态代理模式的实现
- 格式:pdf
- 大小:187.64 KB
- 文档页数:7
Java静态代理与动态代理模式的实现
前⾔: 在现实⽣活中,考虑以下的场景:⼩王打算要去租房,他相中了⼀个房⼦,准备去找房东洽谈相关事宜。但是房东他很忙,平时上班没时间,总没有时间见⾯,他
也没办法。后来,房东想了⼀个办法,他找到了⼀个⼈代替⾃⼰和⼩王洽谈,房东本⼈不⽤出⾯,他只要把他的对房客的要求告诉他找的那个⼈,那个⼈和你商量就可以
了,这样就可以完成租房这件事了。这种现实场景⽐⽐皆是,所呈现出来的其实就是代理模式的原型的⼀种。我们把焦点转向编程,你是否在编程中经常遇见这样⼀个问
题,对于访问某个对象,我们希望给它的⽅法前加⼊⼀个标记,⽐如对象的⽅法开始执⾏、结束等等(⽐如⽇志记录)。怎么办呢,这个时候只要我们编写⼀个复制的类,
然后把这个对象传给这个类,再对这个类进⾏操作,不就可以了吗。这就是代理模式,复制的类就是代理对象,通过代理对象与我们进⾏打交道就可以对它原来的对象进⾏
改造。对于有些时候现有的对象不能满⾜我们的需求的时候,如何对它进⾏扩展,对⽅法进⾏改造,使其适⽤于我们所⾯临的问题,这就是代理模式的思维出发点。
本篇博客的⽬录:
⼀:代理模式的介绍
⼆:实现静态代理
三:代理的进阶:实现动态代理
四:总结
接下来按照⽬录,我们来依次讲解本篇博客:
⼀:代理模式的介绍
1.1:⽬标
为其他对象提供⼀种代理以控制对这个对象的访问
解释:在实际编程中我们会产⽣⼀个代理对象,然后去引⽤被代理对象,对被代理对象进⾏控制与访问,实现客户端对原代理对象的访问,详情见下⾯的代码⽰例。
1.2:适⽤性
在需要⽤⽐较通⽤和复杂的对象指针代替简单的指针的时候,使⽤Proxy 模式。下⾯是⼀ 些可以使⽤Proxy 模式常见情况:
1.2.1:远程代理(Remote Proxy )为⼀个对象在不同的地址空间提供局部代表。 NEXTSTEP[Add94] 使⽤N X P r o x y 类实现了这⼀⽬的。
1.2.2:虚代理(Virtual Proxy )根据需要创建开销很⼤的对象。在动机⼀节描述的I m a g e P r o x y 就是这样⼀种代理的例⼦。
1.2.3: 保护代理(Protection Proxy )控制对原始对象的访问。保护代理⽤于对象应该有不同 的访问权限的时候
1.2.4: 智能指引(Smart Reference )取代了简单的指针,它在访问对象时执⾏⼀些附加操作。
1.3:结构
⼆:实现静态代理
2.1:代码场景
假如我们现在由以下的场景:⽂件编辑器要对⼀个图像⽂件进⾏操作,遵循以下顺序:加载,绘制,获取长度和宽度,存储四个步骤,但是有个问题,需要被加载的图⽚
⾮常⼤,每次加载的时候都要耗费很多时间。并且我们希望对图⽚的操作可以记录出来操作的步骤,⽐如第⼀步、第⼆步这样便于我们去理解。为了解决这个问题,我们可
以先考虑解决第⼀个问题就是利⽤代理模式去新建⼀个代理对象,然后在代理对象⾥去实现⼀个缓存,这样下次我们直接可以去缓存⾥⾯取对象,⽽不⽤去新建,这样就省
去了新建对象消耗的资源。另⼀⽅⾯,我们可以考虑去引⽤原来的⽅法,再给这⽅法基础上添加我们所要做的记录。接下⾥我们⽤java代码来实现这个场景:
2.2:代码⽰范
2.2.1:⾸先新建⼀个接⼝,命名为Graphic,其中主要规范了我们进⾏操作的步骤
public interface Graphic {
void load();//加载
void Draw();//绘制
Extent GetExtent();//获取长度和宽度
void Store();//存储
}2.2.2:然后去新建⼀个Image类,⽤于实现接⼝,对操作进⾏具体控制,注意为了其中的Extent是对宽度和长度的封装(省略get和set⽅法)
public class Image implements Graphic{
public Image() {
try {
Thread.sleep(2000); //模拟创建需要花费很久的时间
System.out.println("正在创建对象");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void load() {
System.out.println("进⾏加载..");
}
@Override
public void Draw() {
System.out.println("进⾏绘画..");
}
@Override
public Extent GetExtent() {
Extent extent = new Extent("100","200");
System.out.println("获取图⽚的属性是:"+extent.toString());
return extent;
}
@Override
public void Store() {
System.out.println("图⽚进⾏存储在硬盘⾥..");
}
}
public class Extent {
private String width;
private String length;
public Extent(String width, String length) {
super();
this.width = width;
this.length = length;
}
//getter And setter⽅法
}
2.2.3:接下来就是很关键的⼀步了,新建我们的代理类,我们新建⼀个类叫做ImageProxy,然后实现缓存与记录的效果:import java.util.HashMap;
import java.util.Map;
public class ImageProxy implements Graphic{
private Image image;
private Map
public ImageProxy() {
init();
}
public void init(){ //只需要初始化⼀次
if (image==null) {
image= new Image();
cache.put("image", image);//放⼊缓存
}else{
image=cache.get("image");
}
@Override
public void load() {
System.out.println("---第⼀步开始---");
image.load();
System.out.println("---第⼀步结束---");
}
@Override
public void Draw() {
System.out.println("---第⼆步开始---");
image.Draw();
System.out.println("---第⼆步结束---");
}
@Override
public Extent GetExtent() {
System.out.println("---第三步开始---");
Extent extent = image.GetExtent();
System.out.println("---第三步结束--");
return extent;
}
@Override
public void Store() {
System.out.println("---第四步开始---");
image.Store();
System.out.println("---第四步结束--");
}
}
2.2.4:我们的⽂档编辑器现在要开始进⾏⽂档编辑了,我们来实现具体的代码,我们先来引⽤⼀下原对象,看⼀下原来的对象会出现什么情况:public class DocumentEditor {
public static void main(String[] args) {
Graphic proxy = new Image();//引⽤代码
proxy.load();
proxy.Draw();
proxy.GetExtent();
proxy.Store();
}
}
2.2.5:测试代码
正在创建对象
进⾏加载..
进⾏绘画..
获取图⽚的属性是:Extent [width=100, length=200]
图⽚进⾏存储在硬盘⾥..
我们可以看出,它会消耗3秒才会出来具体的对象,并且没有我们所需要的记录。好了,我们把2.2.4的引⽤代码改为: Graphic proxy = new ImageProxy();
我们再来测试⼀下:
正在创建对象
---第⼀步开始---
进⾏加载..
---第⼀步结束---
---第⼆步开始---
进⾏绘画..
---第⼆步结束---
---第三步开始---
获取图⽚的属性是:Extent [width=100, length=200]
---第三步结束--
---第四步开始---
图⽚进⾏存储在硬盘⾥..
---第四步结束--
很明显可以看出,通过访问我们的代理对象,就可以实现对原⽅法的改造,这就是代理模式的精髓思想。不过到这⾥你可能会问,为什么不对原对象进⾏改造呢?为什
么要给他新建⼀个代理对象,这不是很⿇烦吗。回答这个问题,⾸先要提⼀个代码的设计原则,也就是有名的开闭原则:对扩展开放,对修改关闭。这句话的意思就是不建
议对原有的代码进⾏修改,我们要做的事就是尽量不⽤动原有的类和对象,在它的基础上去改造,⽽不是直接去修改它。⾄于这个原则为什么这样,我想其中⼀个原因就是
因为软件体系中牵⼀发很动全⾝的事情很常见,很可能你修改了这⼀⼩块,然⽽与此相关的很多东西就会发⽣变化。所以轻易不要修改,⽽是扩展。
三:实现动态代理
3.1:静态代理的不⾜:
通过看静态代理可以动态扩展我们的对象,但是有个问题,在我们进⾏⽅法扩展的时候,⽐如我们的⽇志功能:每个前⾯都得写第⼀步、第⼆步。如果我们要再⼀些其他
的东西,⽐如权限校验、代码说明,⼀个两个⽅法还好,万⼀⽅法成百个呢,那我们岂不是要累死。这就是动态代理要解决的问题,只需要写⼀次就可以,究竟是怎么实现
的呢,接下⾥我们来⼀探究竟吧。
3.2:动态代理的准备:
动态代理需要⽤到JDk的Proxy类,通过它的newProxyInstance()⽅法可以⽣成⼀个代理类,我们来通过jdk看⼀下具体的说明,如何使⽤它:
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
返回⼀个指定接⼝的代理类实例,该接⼝可以将⽅法调⽤指派到指定的调⽤处理程序。此⽅法相当于:
Proxy.getProxyClass(loader, interfaces).
getConstructor(new Class[] { InvocationHandler.class }).
newInstance(new Object[] { handler });