java 代理模式及动态代理机制深入分析

发布时间 - 2026-01-10 23:25:08    点击率:

java 代理模式及动态代理机制深入分析

代理设计模式

       代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

代理模式一般涉及到的角色有:

抽象角色:声明真实对象和代理对象的共同接口;
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便

在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象

图 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 动态代理就是佐例。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!


# java  # 代理模式及动态代理机制  # 代理模式及动态代理机制详细介绍  # Java简单实现动态代理模式过程解析  # Java代理模式实例详解【静态代理与动态代理】  # Java动态代理模式的深入揭秘  # Java设计模式之动态代理模式实例分析  # JAVA动态代理模式(从现实生活角度理解代码原理)  # 详解java动态代理模式  # java代理模式与动态代理模式详解  # 代理模式之Java动态代理实现方法  # Java代理模式与动态代理之间的关系以及概念  # 这是  # 为其  # 自己的  # 实现了  # 是一种  # 第一个  # 让我们  # 他对  # 第三个  # 并能  # 客户端  # 是一个  # 直接调用  # 是在  # 也有  # 很好  # 抽象类  # 子类  # 有很多  # 都能 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 标准网站视频模板制作软件,现在有哪个网站的视频编辑素材最齐全的,背景音乐、音效等?  韩国代理服务器如何选?解析IP设置技巧与跨境访问优化指南  Laravel如何编写单元测试和功能测试?(PHPUnit示例)  英语简历制作免费网站推荐,如何将简历翻译成英文?  详解jQuery中的事件  C++时间戳转换成日期时间的步骤和示例代码  VIVO手机上del键无效OnKeyListener不响应的原因及解决方法  Laravel的Blade指令怎么自定义_创建你自己的Laravel Blade Directives  关于BootStrap modal 在IOS9中不能弹出的解决方法(IOS 9 bootstrap modal ios 9 noticework)  独立制作一个网站多少钱,建立网站需要花多少钱?  如何在阿里云高效完成企业建站全流程?  Laravel怎么实现验证码(Captcha)功能  Laravel如何清理系统缓存命令_Laravel清除路由配置及视图缓存的方法【总结】  微博html5版本怎么弄发语音微博_语音录制入口及时长限制操作【教程】  品牌网站制作公司有哪些,买正品品牌一般去哪个网站买?  百度输入法全感官ai怎么关 百度输入法全感官皮肤关闭  🚀拖拽式CMS建站能否实现高效与个性化并存?  Laravel Sail是什么_基于Docker的Laravel本地开发环境Sail入门  使用spring连接及操作mongodb3.0实例  Android自定义listview布局实现上拉加载下拉刷新功能  如何续费美橙建站之星域名及服务?  如何将凡科建站内容保存为本地文件?  如何用搬瓦工VPS快速搭建个人网站?  Android中AutoCompleteTextView自动提示  Laravel Eloquent关联是什么_Laravel模型一对一与一对多关系精讲  Laravel如何理解并使用服务容器(Service Container)_Laravel依赖注入与容器绑定说明  北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?  个人摄影网站制作流程,摄影爱好者都去什么网站?  香港服务器如何优化才能显著提升网站加载速度?  微信小程序 input输入框控件详解及实例(多种示例)  Laravel怎么实现支付功能_Laravel集成支付宝微信支付  Laravel定时任务怎么设置_Laravel Crontab调度器配置  香港服务器部署网站为何提示未备案?  中国移动官方网站首页入口 中国移动官网网页登录  手机软键盘弹出时影响布局的解决方法  Android中Textview和图片同行显示(文字超出用省略号,图片自动靠右边)  Bootstrap整体框架之JavaScript插件架构  谷歌浏览器如何更改浏览器主题 Google Chrome主题设置教程  Laravel如何实现RSS订阅源功能_Laravel动态生成网站XML格式订阅内容【教程】  Win11怎么查看显卡温度 Win11任务管理器查看GPU温度【技巧】  如何在搬瓦工VPS快速搭建网站?  教你用AI润色文章,让你的文字表达更专业  合肥制作网站的公司有哪些,合肥聚美网络科技有限公司介绍?  C语言设计一个闪闪的圣诞树  如何为不同团队 ID 动态生成多个“认领值班”按钮  如何在Windows服务器上快速搭建网站?  如何在Tomcat中配置并部署网站项目?  邀请函制作网站有哪些,有没有做年会邀请函的网站啊?在线制作,模板很多的那种?  如何用VPS主机快速搭建个人网站?  Python结构化数据采集_字段抽取解析【教程】