Java动态代理实现_动力节点Java学院整理

发布时间 - 2026-01-11 02:37:40    点击率:

动态代理作为代理模式的一种扩展形式,广泛应用于框架(尤其是基于AOP的框架)的设计与开发,本文将通过实例来讲解Java动态代理的实现过程。

通常情况下,代理模式中的每一个代理类在编译之后都会生成一个class文件,代理类所实现的接口和所代理的方法都被固定,这种代理被称之为静态代理(Static Proxy)。那么有没有一种机制能够让系统在运行时动态创建代理类?答案就是本文将要介绍的动态代理(Dynamic Proxy)。动态代理是一种较为高级的代理模式,它在事务管理、AOP(Aspect-OrientedProgramming,面向方面编程)等领域都发挥了重要的作用。

在传统的代理模式中,客户端通过Proxy类调用RealSubject类的request()方法,同时还可以在代理类中封装其他方法(如preRequest()和postRequest()等)。如果按照这种方法使用代理模式,那么代理类和真实主题类都应该是事先已经存在的,代理类的接口和所代理方法都已明确指定,如果需要为不同的真实主题类提供代理类或者代理一个真实主题类中的不同方法,都需要增加新的代理类,这将导致系统中的类个数急剧增加,因此需要想办法减少系统中类的个数。动态代理可以让系统能够根据实际需要来动态创建代理类,让同一个代理类能够代理多个不同的真实主题类而且可以代理不同的方法。

从JDK 1.3开始,Java语言提供了对动态代理的支持,Java语言实现动态代理时需要用到位于java.lang.reflect包中的一些类,现简要说明如下:

 (1) Proxy类

 Proxy类提供了用于创建动态代理类和实例对象的方法,它是所创建的动态代理类的父类,它最常用的方法如下:

  • public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces):该方法用于返回一个Class类型的代理类,在参数中需要提供类加载器并需要指定代理的接口数组(与真实主题类的接口列表一致)。
  • public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces, InvocationHandler h):该方法用于返回一个动态创建的代理类的实例,方法中第一个参数loader表示代理类的类加载器,第二个参数interfaces表示代理类所实现的接口列表(与真实主题类的接口列表一致),第三个参数h表示所指派的调用处理程序类。

 (2) InvocationHandler接口

InvocationHandler接口是代理处理程序类的实现接口,该接口作为代理实例的调用处理者的公共父类,每一个代理类的实例都可以提供一个相关的具体调用处理者(InvocationHandler接口的子类)。在该接口中声明了如下方法:

public Object invoke(Objectproxy, Method method, Object[] args):该方法用于处理对代理类实例的方法调用并返回相应的结果,当一个代理实例中的业务方法被调用时将自动调用该方法。invoke()方法包含三个参数,其中第一个参数proxy表示代理类的实例,第二个参数method表示需要代理的方法,第三个参数args表示代理方法的参数数组。

动态代理类需要在运行时指定所代理真实主题类的接口,客户端在调用动态代理对象的方法时,调用请求会将请求自动转发给InvocationHandler对象的invoke()方法,由invoke()方法来实现对请求的统一处理

下面通过一个简单实例来学习如何使用动态代理模式:

Sunny软件公司欲为公司OA系统数据访问层DAO增加方法调用日志,记录每一个方法被调用的时间和调用结果,现使用动态代理进行设计和实现。

本实例完整代码如下所示:

import java.lang.reflect.Proxy; 
import java.lang.reflect.InvocationHandler; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 
import java.util.Calendar; 
import java.util.GregorianCalendar; 
 
//抽象UserDAO:抽象主题角色 
interface AbstractUserDAO { 
  public Boolean findUserById(String userId); 
} 
 
//抽象DocumentDAO:抽象主题角色 
interface AbstractDocumentDAO { 
  public Boolean deleteDocumentById(String documentId); 
} 
 
//具体UserDAO类:真实主题角色 
class UserDAO implements AbstractUserDAO { 
  public Boolean findUserById(String userId) { 
    if (userId.equalsIgnoreCase("张无忌")) { 
      System.out.println("查询ID为" + userId + "的用户信息成功!"); 
      return true; 
    } 
    else { 
      System.out.println("查询ID为" + userId + "的用户信息失败!"); 
      return false; 
    } 
  } 
} 
 
//具体DocumentDAO类:真实主题角色 
class DocumentDAO implements AbstractDocumentDAO { 
  public Boolean deleteDocumentById(String documentId) { 
    if (documentId.equalsIgnoreCase("D001")) { 
      System.out.println("删除ID为" + documentId + "的文档信息成功!"); 
      return true; 
    } 
    else { 
      System.out.println("删除ID为" + documentId + "的文档信息失败!"); 
      return false; 
    } 
  } 
} 
 
//自定义请求处理程序类 
class DAOLogHandler implements InvocationHandler { 
  private Calendar calendar; 
  private Object object; 
   
  public DAOLogHandler() {   
  } 
   
  //自定义有参构造函数,用于注入一个需要提供代理的真实主题对象 
  public DAOLogHandler(Object object) { 
    this.object = object; 
  } 
   
  //实现invoke()方法,调用在真实主题类中定义的方法 
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
    beforeInvoke(); 
    Object result = method.invoke(object, args); //转发调用 
    afterInvoke(); 
    return null; 
  } 
 
  //记录方法调用时间 
  public void beforeInvoke(){ 
    calendar = new GregorianCalendar(); 
    int hour = calendar.get(Calendar.HOUR_OF_DAY); 
    int minute = calendar.get(Calendar.MINUTE); 
    int second = calendar.get(Calendar.SECOND); 
    String time = hour + ":" + minute + ":" + second; 
    System.out.println("调用时间:" + time); 
  } 
 
  public void afterInvoke(){ 
    System.out.println("方法调用结束!" ); 
  } 
} 

编写如下客户端测试代码:

class Client { 
  public static void main(String args[]) { 
    InvocationHandler handler = null; 
     
    AbstractUserDAO userDAO = new UserDAO(); 
    handler = new DAOLogHandler(userDAO); 
    AbstractUserDAO proxy = null; 
    //动态创建代理对象,用于代理一个AbstractUserDAO类型的真实主题对象 
    proxy = (AbstractUserDAO)Proxy.newProxyInstance(AbstractUserDAO. class.getClassLoader(), new Class[]{AbstractUserDAO.class}, handler); 
    proxy.findUserById("张无忌"); //调用代理对象的业务方法 
   
    System.out.println("------------------------------"); 
   
    AbstractDocumentDAO docDAO = new DocumentDAO(); 
    handler = new DAOLogHandler(docDAO); 
    AbstractDocumentDAO proxy_new = null; 
//动态创建代理对象,用于代理一个AbstractDocumentDAO类型的真实主题对象 
    proxy_new = (AbstractDocumentDAO)Proxy.newProxyInstance(Abstract DocumentDAO.class.getClassLoader(), new Class[]{AbstractDocumentDAO.class}, handler); 
    proxy_new.deleteDocumentById("D002"); //调用代理对象的业务方法 
  }  
} 

编译并运行程序,输出结果如下:

调用时间:13:47:14
查询ID为张无忌的用户信息成功!
方法调用结束!
------------------------------
调用时间:13:47:14
删除ID为D002的文档信息失败!
方法调用结束!

通过使用动态代理,我们可以实现对多个真实主题类的统一代理和集中控制。

注:JDK中提供的动态代理只能代理一个或多个接口,如果需要动态代理具体类或抽象类,可以使用CGLib(Code Generation Library)等工具,CGLib是一个功能较为强大、性能和质量也较好的代码生成包,在许多AOP框架中都得以广泛应用,大家可以自行查阅相关资料来学习CGLib。


# java动态代理的实现  # java  # 动态代理实现  # 动态代理实现原理  # 浅谈Java注解和动态代理  # 详解Java动态代理的实现及应用  # JAVA中的静态代理、动态代理以及CGLIB动态代理总结  # java 1.8 动态代理源码深度分析  # Java动态代理机制详解_动力节点Java学院整理  # java 中动态代理(JDK  # cglib)实例代码  # Java中反射动态代理接口的详解及实例  # Java 动态代理原理分析  # 多个  # 第一个  # 类中  # 张无忌  # 客户端  # 第二个  # 自定义  # 文档  # 第三个  # 是一个  # 表一  # 加载  # 是一种  # 集中控制  # 还可以  # 子类  # 它是  # 较好  # 要用  # 可以使用 


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


相关推荐: Linux系统命令中tree命令详解  Win11怎么关闭资讯和兴趣_Windows11任务栏设置隐藏小组件  php增删改查怎么学_零基础入门php数据库操作必知基础【教程】  如何快速搭建高效WAP手机网站?  千库网官网入口推荐 千库网设计创意平台入口  如何用腾讯建站主机快速创建免费网站?  如何有效防御Web建站篡改攻击?  如何用ChatGPT准备面试 模拟面试问答与职场话术练习教程  如何登录建站主机?访问步骤全解析  Laravel的Blade指令怎么自定义_创建你自己的Laravel Blade Directives  专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?  Laravel如何使用Livewire构建动态组件?(入门代码)  制作公司内部网站有哪些,内网如何建网站?  网站制作免费,什么网站能看正片电影?  zabbix利用python脚本发送报警邮件的方法  Laravel如何实现数据库事务?(DB Facade示例)  如何在万网ECS上快速搭建专属网站?  Laravel中Service Container是做什么的_Laravel服务容器与依赖注入核心概念解析  Laravel如何生成URL和重定向?(路由助手函数)  html5的keygen标签为什么废弃_替代方案说明【解答】  香港服务器网站搭建教程-电商部署、配置优化与安全稳定指南  Win11怎么更改系统语言为中文_Windows11安装语言包并设为显示语言  Windows10如何更改计算机工作组_Win10系统属性修改Workgroup  简单实现jsp分页  EditPlus中的正则表达式实战(5)  教你用AI将一段旋律扩展成一首完整的曲子  在线制作视频的网站有哪些,电脑如何制作视频短片?  Laravel如何获取当前用户信息_Laravel Auth门面获取用户ID  Laravel怎么在Blade中安全地输出原始HTML内容  如何快速配置高效服务器建站软件?  JavaScript如何实现继承_有哪些常用方法  如何在服务器上配置二级域名建站?  MySQL查询结果复制到新表的方法(更新、插入)  Laravel如何使用Service Container和依赖注入?(代码示例)  Laravel如何实现本地化和多语言支持?(i18n教程)  php 三元运算符实例详细介绍  清除minerd进程的简单方法  如何在Windows 2008云服务器安全搭建网站?  Laravel怎么做数据加密_Laravel内置Crypt门面的加密与解密功能  Laravel如何优化应用性能?(缓存和优化命令)  EditPlus中的正则表达式 实战(4)  如何彻底卸载建站之星软件?  如何正确下载安装西数主机建站助手?  Laravel如何实现多语言支持_Laravel本地化与国际化(i18n)配置教程  如何用景安虚拟主机手机版绑定域名建站?  如何在阿里云完成域名注册与建站?  实现点击下箭头变上箭头来回切换的两种方法【推荐】  Win11搜索栏无法输入_解决Win11开始菜单搜索没反应问题【技巧】  如何在局域网内绑定自建网站域名?  HTML透明颜色代码怎么让下拉菜单透明_下拉菜单透明背景指南【技巧】