您的位置:首页 > 编程语言 > Java开发

Java代理模式(静态模式&动态模式简介)

2016-07-15 18:17 351 查看
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。意图:为其他对象提供一种代理以控制对这个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。更通俗的说,代理解决的问题当两个类需要通信时,引入第三方代理类,将两个类的关系解耦,让我们只了解代理类即可,而且代理的出现还可以让我们完成与另一个类之间的关系的统一管理,但是切记,代理类和委托类要实现相同的接口,因为代理真正调用的还是委托类的方法。主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。·何时使用:想在访问一个类时做一些控制。·如何解决:增加中间层。·关键代码:实现与被代理类组合。·优点: 1、职责清晰。 2、高扩展性。 3、智能化。·缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。使用场景:按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。·注意事项: 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
静态代理&动态代理按照代理的创建时期,代理类可以分为两种: 静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。动态:在程序运行时运用反射机制动态创建而成。

#静态代理
·优点:代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合)。 ·缺点:1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserManager类的访问提供了代理,但是如果还要为其他类如Department类提供代理的话,就需要我们再次添加代理Department的代理类。 举例说明:代理可以对实现类进行统一的管理,如在调用具体实现类之前,需要打印日志等信息,这样我们只需要添加一个代理类,在代理类中添加打印日志的功能,然后调用实现类,这样就避免了修改具体实现类。满足我们所说的开闭原则。但是如果想让每个实现类都添加打印日志的功能的话,就需要添加多个代理类,以及代理类中各个方法都需要添加打印日志功能(如上的代理方法中删除,修改,以及查询都需要添加上打印日志的功能)即静态代理类只能为特定的接口(Service)服务。如想要为多个接口服务则需要建立很多个代理类。

使用场合举例:如:假设现在需要一个专门用来加载图片(LoadFromDisk)并展示(Display)的类。但是在展示图片的前后可能需要判断(Judge)一些其他信息是否符合要求,如果不符合,则不加载。
1.接口类或抽象类
public interface Image {
void display();
}
//或
abstract class Image{
public abstract void display();
}


2.真正实现功能的类
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
//具体展示功能
}
private void loadFromDisk(String fileName){
//从磁盘加载
}
}


3.静态代理(1)ProxyImage内部有对象为RealImages时,如下所示。
public class ProxyImage implements Image{
private Image image;
public ProxyImage(Image image){    // 通过构造方法传入目标对象
this.image=image;
}
public boolean Judge(){
//检查条件
}

@Override
public void display() {
if(Judge()){
image.display();
}
}
}
具体使用:
Image image = new ProxyImage("test.jpg");
image.display(); //先调用ProxyImage的display方法,先经过Judge()判断,如果符合条件,则调用realImage的display方法。


(2)ProxyImage中的私有对象为Image。
public class ProxyImage implements Image{
private Image image;
public ProxyImage(Image image){    // 通过构造方法传入目标对象
this.image=image;
}
public boolean Judge(){
//检查条件
}

@Override
public void display() {
if(Judge()){
image.display();
}
}
}

具体使用:
Image image = new ProxyImage(new RealImage("test.jpg"));
image.display(); //先调用ProxyImage的display方法,先经过Judge()判断,如果符合条件,则调用realImage


#动态代理在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 类的支持动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke方法。该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法返回的值是被代理接口的一个实现类
静态代理中,每个代理类只能为一个接口服务,代理一种类型,而且是在编译器就已经确定被代理的对象。这样程序开发中必然会产生许多的代理类。所以我们就会想办法可以,在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象,通过一个代理类完成全部的代理功能。
public class ProxyHandler implements InvocationHandler {
// 目标对象
private Object targetObject;
//绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
public Object newProxyInstance(Object targetObject){
this.targetObject=targetObject;
//该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
//第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
//第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
//第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
//根据传入的目标返回一个代理对象
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),this);
}

public boolean Judge(){
//判断条件
}

@Override
//关联的这个实现类的方法被调用时将被执行
/*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//此处可能会有一些预处理操作
if(Judge(){       //判断是否符合条件
Object ret=null;
try{
//调用目标方法
ret=method.invoke(targetObject, args);
}catch(Exception e){
e.printStackTrace();
throw e;
}
return ret;
}
//此处可能会有一些后续处理操作
}
}


具体实现:
ProxyHandler roxyHandler=new ProxyHandler();
Image image=(Image)proxyHandler.newProxyInstance(new RealImage());
image.display();


参考资料:

菜鸟教程|代理模式:http://www.runoob.com/design-pattern/proxy-pattern.html
何静媛的CSDN博客:《 JAVA学习篇--静态代理VS动态代理》http://blog.csdn.net/hejingyuan6/article/details/36203505
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: