热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java程序员从笨鸟到菜鸟之(三十九)大话设计模式(七)代理模式和java动态代理机制

本文来自:曹胜欢博客专栏。转载请注明出处:http:blog.csdn.netcsh624366188代理设计模式代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制

本文来自:曹胜欢博客专栏。转载请注明出处:http://blog.csdn.net/csh624366188

 

代理设计模式

       代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式一般涉及到的角色有:
抽象角色:声明真实对象和代理对象的共同接口;
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象
图 1. 代理模式类图


为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托 类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。

java动态代理

相关的类和接口

要了解 Java 动态代理的机制,首先需要了解以下相关的类或接口:

· java.lang.reflect.Proxy:这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。 

清单 1. Proxy 的静态方法

// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器

static InvocationHandler getInvocationHandler(Object proxy)

// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象

static Class getProxyClass(ClassLoader loader, Class[] interfaces)

// 方法 3:该方法用于判断指定类对象是否是一个动态代理类

static boolean isProxyClass(Class cl)

// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例

static Object newProxyInstance(ClassLoader loader, Class[] interfaces,

InvocationHandler h)


java.lang.reflect.InvocationHandler:这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。 

清单 2. InvocationHandler 的核心方法

// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象

// 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行

Object invoke(Object proxy, Method method, Object[] args)


每次生成动态代理类对象时都需要指定一个实现了该接口的调用处理器对象(参见 Proxy 静态方法 4 的第三个参数)。

· java.lang.ClassLoader:这是类装载器类,负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何个 .class 文件中。 

每次生成动态代理类对象时都需要指定一个类装载器对象(参见 Proxy 静态方法 4 的第一个参数)

代理机制及其特点

首先让我们来了解一下如何使用 Java 动态代理。具体有如下四步骤:

1. 通过实现 InvocationHandler 接口创建自己的调用处理器;

2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;

3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。


清单 3. 动态代理对象创建过程

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发

// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用

InvocationHandler handler = new InvocationHandlerImpl(..);


// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象

Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });


// 通过反射从生成的类对象获得构造函数对象

Constructor cOnstructor= clazz.getConstructor(new Class[] { InvocationHandler.class });


// 通过构造函数对象创建动态代理类实例

Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });


实际使用过程更加简单,因为 Proxy 的静态方法 newProxyInstance 已经为我们封装了步骤 2 到步骤 4 的过程,所以简化后的过程如
清单 4. 简化的动态代理对象创建过程

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发

InvocationHandler handler = new InvocationHandlerImpl(..);

// 通过 Proxy 直接创建动态代理类实例

Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,

new Class[] { Interface.class },

handler );


下面我们来看一个简单实现动态代理的例子:

1.代理类和真实类接口:

public interface Subject

{

public void request();

}


2.真实类:

public class RealSubject implements Subject

{

public void request()

{

System.out.println("From real subject!");

}}


3.具体代理类:

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

public class DynamicSubject implements InvocationHandler

{

private Object sub;

public DynamicSubject(Object obj)

{

this.sub = obj;

}

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable

{

System.out.println("before calling: " + method);

method.invoke(sub, args);

System.out.println(args == null);

System.out.println("after calling: " + method);

return null;

}


注:该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个对象。 此外,该类还实现了invoke方法,该方法中的method.invoke其实就是调用被代理对象的将要 执行的方法,方法参数是sub,表示该方法从属于sub,通过动态代理类,我们可以在执行真实对象的方法前后加入自己的一些额外方法。

4.客户端调用示例:

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Proxy;

public class Client

{

public static void main(String[] args)

{

RealSubject realSubject = new RealSubject();

InvocationHandler handler = new DynamicSubject(realSubject);

Class classType = handler.getClass();

// 下面的代码一次性生成代理

Subject subject = (Subject) Proxy.newProxyInstance(classType

.getClassLoader(), realSubject.getClass().getInterfaces(),

handler);

subject.request();

System.out.println(subject.getClass());

}

}


接下来让我们来了解一下 Java 动态代理机制 Proxy 的构造方法:
清单 6. Proxy 构造方法

// 由于 Proxy 内部从不直接调用构造函数,所以 private 类型意味着禁止任何调用

private Proxy() {}


// 由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用

protected Proxy(InvocationHandler h) {this.h = h;}


接着,可以快速浏览一下 newProxyInstance 方法,因为其相当简单:


清单 7. Proxy 静态方法 newProxyInstance

public static Object newProxyInstance(ClassLoader loader, 

Class[] interfaces,

InvocationHandler h)

throws IllegalArgumentException {



// 检查 h 不为空,否则抛异常

if (h == null) {

throw new NullPointerException();

}

// 获得与制定类装载器和一组接口相关的代理类类型对象

Class cl = getProxyClass(loader, interfaces);


// 通过反射获取构造函数对象并生成代理类实例

try {

Constructor cOns= cl.getConstructor(constructorParams);

return (Object) cons.newInstance(new Object[] { h });

} catch (NoSuchMethodException e) { throw new InternalError(e.toString());

} catch (IllegalAccessException e) { throw new InternalError(e.toString());

} catch (InstantiationException e) { throw new InternalError(e.toString());

} catch (InvocationTargetException e) { throw new InternalError(e.toString());

}
}


     由此可见,动态代理真正的关键是在 getProxyClass 方法,该方法负责为一组接口动态地生成代理类类型对象。

     有很多条理由,人们可以否定对 class 代理的必要性,但是同样有一些理由,相信支持 class 动态代理会更美好。接口和类的划分,本就不是很明显,只是到了 Java 中才变得如此的细化。如果只从方法的声明及是否被定义来考量,有一种两者的混合体,它的名字叫抽象类。实现对抽象类的动态代理,相信也有其内在的价值。此 外,还有一些历史遗留的类,它们将因为没有实现任何接口而从此与动态代理永世无缘。如此种种,不得不说是一个小小的遗憾。

但是,不完美并不等于不伟大,伟大是一种本质,Java 动态代理就是佐例。


推荐阅读
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • Java自带的观察者模式及实现方法详解
    本文介绍了Java自带的观察者模式,包括Observer和Observable对象的定义和使用方法。通过添加观察者和设置内部标志位,当被观察者中的事件发生变化时,通知观察者对象并执行相应的操作。实现观察者模式非常简单,只需继承Observable类和实现Observer接口即可。详情请参考Java官方api文档。 ... [详细]
author-avatar
筱冬瀦
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有