java动态代理的实现

动态代理作为代理模式的一种扩展形式,广泛应用于框架(尤其是基于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增加方法调用日志,记录每一个方法被调用的时间和调用结果,现使用动态代理进行设计和实现。

 

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

[java] view plain copy
  1. import java.lang.reflect.Proxy;  
  2. import java.lang.reflect.InvocationHandler;  
  3. import java.lang.reflect.InvocationTargetException;  
  4. import java.lang.reflect.Method;  
  5. import java.util.Calendar;  
  6. import java.util.GregorianCalendar;  
  7.   
  8. //抽象UserDAO:抽象主题角色  
  9. interface AbstractUserDAO {  
  10.     public Boolean findUserById(String userId);  
  11. }  
  12.   
  13. //抽象DocumentDAO:抽象主题角色  
  14. interface AbstractDocumentDAO {  
  15.     public Boolean deleteDocumentById(String documentId);  
  16. }  
  17.   
  18. //具体UserDAO类:真实主题角色  
  19. class UserDAO implements AbstractUserDAO {  
  20.     public Boolean findUserById(String userId) {  
  21.         if (userId.equalsIgnoreCase("张无忌")) {  
  22.             System.out.println("查询ID为" + userId + "的用户信息成功!");  
  23.             return true;  
  24.         }  
  25.         else {  
  26.             System.out.println("查询ID为" + userId + "的用户信息失败!");  
  27.             return false;  
  28.         }  
  29.     }  
  30. }  
  31.   
  32. //具体DocumentDAO类:真实主题角色  
  33. class DocumentDAO implements AbstractDocumentDAO {  
  34.     public Boolean deleteDocumentById(String documentId) {  
  35.         if (documentId.equalsIgnoreCase("D001")) {  
  36.             System.out.println("删除ID为" + documentId + "的文档信息成功!");  
  37.             return true;  
  38.         }  
  39.         else {  
  40.             System.out.println("删除ID为" + documentId + "的文档信息失败!");  
  41.             return false;  
  42.         }  
  43.     }  
  44. }  
  45.   
  46. //自定义请求处理程序类  
  47. class DAOLogHandler implements InvocationHandler {  
  48.     private Calendar calendar;  
  49.     private Object object;  
  50.       
  51.     public DAOLogHandler() {      
  52.     }  
  53.       
  54.     //自定义有参构造函数,用于注入一个需要提供代理的真实主题对象  
  55.     public DAOLogHandler(Object object) {  
  56.         this.object = object;  
  57.     }  
  58.       
  59.     //实现invoke()方法,调用在真实主题类中定义的方法  
  60.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  61.         beforeInvoke();  
  62.         Object result = method.invoke(object, args); //转发调用  
  63.         afterInvoke();  
  64.         return null;  
  65.     }  
  66.   
  67.     //记录方法调用时间  
  68.     public void beforeInvoke(){  
  69.         calendar = new GregorianCalendar();  
  70.         int hour = calendar.get(Calendar.HOUR_OF_DAY);  
  71.         int minute = calendar.get(Calendar.MINUTE);  
  72.         int second = calendar.get(Calendar.SECOND);  
  73.         String time = hour + ":" + minute + ":" + second;  
  74.         System.out.println("调用时间:" + time);  
  75.     }  
  76.   
  77.     public void afterInvoke(){  
  78.         System.out.println("方法调用结束!" );  
  79.     }  
  80. }  

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

[java] view plain copy
  1. class Client {  
  2.     public static void main(String args[]) {  
  3.         InvocationHandler handler = null;  
  4.           
  5.         AbstractUserDAO userDAO = new UserDAO();  
  6.         handler = new DAOLogHandler(userDAO);  
  7.         AbstractUserDAO proxy = null;  
  8.         //动态创建代理对象,用于代理一个AbstractUserDAO类型的真实主题对象  
  9.         proxy = (AbstractUserDAO)Proxy.newProxyInstance(AbstractUserDAO. class.getClassLoader(), new Class[]{AbstractUserDAO.class}, handler);  
  10.         proxy.findUserById("张无忌"); //调用代理对象的业务方法  
  11.       
  12.         System.out.println("------------------------------");  
  13.       
  14.         AbstractDocumentDAO docDAO = new DocumentDAO();  
  15.         handler = new DAOLogHandler(docDAO);  
  16.         AbstractDocumentDAO proxy_new = null;  
  17. //动态创建代理对象,用于代理一个AbstractDocumentDAO类型的真实主题对象  
  18.         proxy_new = (AbstractDocumentDAO)Proxy.newProxyInstance(Abstract DocumentDAO.class.getClassLoader(), new Class[]{AbstractDocumentDAO.class}, handler);  
  19.         proxy_new.deleteDocumentById("D002"); //调用代理对象的业务方法  
  20.     }   
  21. }  

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

调用时间:13:47:14

查询ID为张无忌的用户信息成功!

方法调用结束!

------------------------------

调用时间:13:47:14

删除ID为D002的文档信息失败!

方法调用结束!

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/458987.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

常见的网络类型

*************************************************** 更多精彩&#xff0c;欢迎进入&#xff1a;http://shop115376623.taobao.com *************************************************** 我们经常听到Internet网、星形网等名词&#xff0c;它们表示什么&#xff1f;是怎样分…

oracle放在内存里,oracle如中何把小表钉住在内存中

buffer_pool_defualtbuffer_pool_keepbuffer_pool_recycle如果要把表钉死在内存中&#xff0c;也就是把表钉在keep区。相关的命令为&#xff1a;alter table ..... storage(buffer_pool keep);这句命令把表示表如果缓存的话是缓存在keep区。可以通过语句&#xff1a;select tab…

C++基础之this指针的详解

*************************************************** 更多精彩&#xff0c;欢迎进入&#xff1a;http://shop115376623.taobao.com *************************************************** 关于C中的this指针&#xff0c;建议大家看看这篇文章&#xff0c;《C中的this指针》&a…

xcode 开发ios兼容性问题的上下黑边 和 coco2d-x 游戏分辨率适配 ResolutionPolicy::FIXED_WIDTH 都会引起上下黑边问题!!!...

1&#xff1a;Xcode6在iPhone5iOS7模拟器上编译&#xff0c;上下有黑边问题 问题描述&#xff1a; Xcode6环境下&#xff0c;对iPhone5或iPhone5s模拟器&#xff0c;在iOS7或iOS7.1下运行&#xff0c;屏幕上下有黑边。在iOS8下没问题。 问题分析&#xff1a; 这种熟悉的场景又出…

如何用参数化SQL语句污染你的计划缓存

你的SQL语句的参数化总是个好想法。使用参数化SQL语句你不会污染你的计划缓存——错&#xff01;&#xff01;&#xff01;在这篇文章里我想向你展示下用参数化SQL语句就可以污染你的计划缓存&#xff0c;这是非常简单的&#xff01; ADO.NET-AddWithValue ADO.NET是实现像SQL …

oracle values函数,Oracle文本函数简介

Oracle文本函数使我们常用的函数&#xff0c;下面就为您介绍几种Oracle文本函数的用法&#xff0c;供您参考学习&#xff0c;希望可以让您对Oracle文本函数有更深的认识。(1)UPPER、LOWER和INITCAP这三个函数更改提供给它们的文体的大小写。select upper(product_name) from pr…

c++ int *p = new int()

*************************************************** 更多精彩&#xff0c;欢迎进入&#xff1a;http://shop115376623.taobao.com *************************************************** int *anew int(120); 申请一个整型变量空间&#xff0c;赋初值为120&#xff0c;并…

Ios: 如何保護iOS束文件屬性列表,圖像,SQLite,媒體文件

Ios: 如何保護iOS束文件屬性列表&#xff0c;圖像&#xff0c;SQLite&#xff0c;媒體文件我創建了Hello World示例項目&#xff0c;然後添加data.plist文件到資源文件夾。現在人們可以很容易得到束文件解壓縮。國際音標。有任何的方法來保護data.plist文件保存在iPhone應用程序…

w3wp oracle,w3wp.exe占用CPU超过50%的处理

w3wp.exe占用CPU超过50%的处理1.查看CPU占用高的进程&#xff1a;任务管理器C:\Documents andSettings\Administrator>iisappW3WP.exe PID: 18008 AppPoolId: STATW3WP.exe PID: 8328 AppPoolId: STATW3WP.exe PID: 17868 AppPoolId: JYCV16W3WP.exe PID: 16652 AppPoolId: …

论两种学习模式

引言 A&#xff1a;你是如何学习的&#xff0c;通过视频、书籍和实践结合&#xff1f;B&#xff1a;不是&#xff0c;一般情况是以一个问题为点去画线和面。 两种学习模式 按部就班方式获取知识(通过书、视频)缺点 信息接收者缺乏深度思考和探索信息发布者的知识体系不一定适合…

启动mq命令 linux,RocketMQ:Linux下启动server和broker的命令

目录QUESTION:RocketMQ&#xff1a;Linux下启动server和broker的命令?ANSWER:一、启动mqnamesrv1.1当前执行1.2后台运行二、启动mqbroker2.1当前执行2.2后台运行QUESTION:RocketMQ&#xff1a;Linux下启动server和broker的命令?ANSWER:一、启动mqnamesrv1.1当前执行进入rocke…

C++中int *p[4]和 int (*q)[4]的区别

*************************************************** 更多精彩&#xff0c;欢迎进入&#xff1a;http://shop115376623.taobao.com *************************************************** C中int *p[4]和 int (*q)[4]的区别 前者是指针数组&#xff0c;后者是指向数组的指针…

Spring笔记③--spring的命名空间

p:命名空间: xmlns:p"http://www.springframework.org/schema/p" 作用:简化在xml配置bean的属性 在<bean>中使用p:属性名来配置 AOP:命名空间: xmlns:aop"http://www.springframework.org/schema/aop" xsi:schemaLocation: http://www.springframewo…

linux不用命令开启ssh,不用密码也能ssh登陆Linux?

Linux的一个后门引发对PAM的探究1.1 起因今天在搜索关于Linux下的后门姿势时&#xff0c;发现一条命令如下&#xff1a;ln -sf /usr/sbin/sshd /tmp/su; /tmp/su -oPort5555;经典后门。直接对sshd建立软连接&#xff0c;之后用任意密码登录即可。ssh rootx.x.x.x -p 5555这个是…

ScrollView常用(暂时用上了的)代理方法

2019独角兽企业重金招聘Python工程师标准>>> ScrollView常用代理方法: #pragma mark - 滚动结束调用 -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {// 计算 滑动到了第几页double page scrollView.contentOffset.x / scrollView.width;self.p…

笔试题目汇总

C常考笔试题:不用if,while,do-while,for,打印出所有大于0小于k的整数.函数原型void printLess(int k); 解法一:递归方式(刚想出来) [cpp] view plaincopy #include <iostream> using namespace std; void printLess(int k) { switch(--k) { case 0:…

Android ListView性能优化实例讲解

前言&#xff1a; 对于ListView&#xff0c;大家绝对都不会陌生&#xff0c;只要是做过Android开发的人&#xff0c;哪有不用ListView的呢&#xff1f; 只要是用过ListView的人&#xff0c;哪有不关心对它性能优化的呢&#xff1f; 关于如何对ListView进行性能优化&#xff0c;…

Bzoj 3289: Mato的文件管理 莫队,树状数组,逆序对,离散化,分块

3289: Mato的文件管理 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 1539 Solved: 665[Submit][Status][Discuss]Description Mato同学从各路神犇以各种方式&#xff08;你们懂的&#xff09;收集了许多资料&#xff0c;这些资料一共有n份&#xff0c;每份有一个大小和一…

linux头文件 库,Linux操作系统的头文件和库文件搜索路径

一、 头文件1 “”中的头文件&#xff0c;在源文件当前目录查找2 -I 中指定目录 -I可以在CFLAG中指定3 gcc的环境变量 C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, OBJC_INCLUDE_PATH4 编译器预设路径、内定目录&#xff1a;/usr/include/usr/local/include/usr/lib/gcc-lib/i386-lin…

vs2010创建和使用动态链接库(dll)

*************************************************** 更多精彩&#xff0c;欢迎进入&#xff1a;http://shop115376623.taobao.com *************************************************** 本文将创建一个简单的动态链接库&#xff0c;并编写一个应用台控制程序使用该动态链接…