一、概述代理是一种设计模式,其目的是为其他对象提供一个代理以控制对某个对象的访问,代理类负责为委托类预处理消息,过滤消息并转发消息以及进行消息被委托类执行后的后续处理。为了保持行为的一致性,代理类和委托类通常会实现相同的接口。按照代理的创建时期,代理类可分为两种:静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译,也就是说在程序运行前代理类的.class文件就已经存在。动态代理:在程序运行时运用反射机制动态创建生成。下面在将动态代理的实现机制之前先简单介绍一下静态代理。二、静态代理上面说过,代理类和委托类一般都要实现相同的接口,下面先定义这个接口:public interface Service {     public void add(); } 委托类作为接口的一种实现,定义如下:public class ServiceImpl implements Service { public void add() { System.out.println("添加用户!"); } } 假如我们要对委托类加一些日志的操作,代理类可做如下定义:public class ServiceProxy implements Service { private Service service; public ServiceProxy(Service service) { super(); this.service = service; } public void add() { System.out.println("服务开始"); service.add(); System.out.println("服务结束"); } } 编写测试类:public class TestMain { public static void main(String[] args) { Service serviceImpl=new ServiceImpl(); Service proxy=new ServiceProxy(serviceImpl); proxy.add(); } } 运行测试程序,结果如下图:
从上面的代码可以看到,静态代理类只能为特定的接口服务,如果要服务多类型的对象,就要为每一种对象进行代理。我们就会想是否可以通过一个代理类完成全部的代理功能,于是引入的动态代理的概念。三、动态代理Java的动态代理主要涉及两个类,Proxy和InvocationHandler。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) InvocationHandler:它是调用处理器接口,自定义了一个invok方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象// 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
    Object invoke(Object proxy, Method method, Object[] args) 实现Java的动态代理,具体有以下四个步骤:通过实现InvocationHandler接口创建自己的调用处理器通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理类通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器类接口类型通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入下面根据上述的四个步骤来实现自己的动态代理的示例:接口和接口的实现类(即委托类)跟上面静态代理的代码一样,这里我们来实现InvocationHandler接口创建自己的调用处理器public class ServiceHandle implements InvocationHandler { private Object s; public ServiceHandle(Object s) { this.s = s; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("服务开始"); //invoke表示对带有指定参数的指定对象调用由此 Method 对象表示的底层方法 Object result=method.invoke(s, args); System.out.println("服务结束"); return result; } } 编写测试类:public class TestMain { public static void main(String[] args) { Service service=new ServiceImpl(); InvocationHandler handler=new ServiceHandle(service); Service s=(Service) Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces(), handler); s.add(); } } 运行测试程序,结果同静态代理。我们可以看到上述代码并没有我们之前说的步骤2和3,这是因为Prox的静态方法newProxyInstance已经为我们封装了这两个步骤。具体的内部实现如下:// 通过 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 }); newProxyInstance函数的内部实现为:public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException { //检查h不为空,否则抛异常
                Objects.requireNonNull(h); //获得与制定类装载器和一组接口相关的代理类类型对象 final Class<?>[] intfs = interfaces.clone(); //检查接口类对象是否对类装载器可见并且与类装载器所能识别的接口类对象是完全相同的 final SecurityManager sm = System.getSecurityManager();     if (sm != null)  { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } //获得与制定类装载器和一组接口相关的代理类类型对象 Class<?> cl = getProxyClass0(loader, intfs); try { if (sm != null)  { checkNewProxyPermission(Reflection.getCallerClass(), cl); } // 通过反射获取构造函数对象并生成代理类实例 final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers()))  { AccessController.doPrivileged(new PrivilegedAction<Void>()  { public Void run()  { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); }  catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e)  { Throwable t = e.getCause(); if (t instanceof RuntimeException)  { throw (RuntimeException) t; } else  { throw new InternalError(t.toString(), t); } }  catch (NoSuchMethodException e)  { throw new InternalError(e.toString(), e); } } 四、模拟实现Proxy类根据上面的原理介绍,我们可以自己模拟实现Proxy类:public class Proxy { public static Object newProxyInstance(Class inface,InvocationHandle h) throws Exception { String rt="\r\n"; String methodStr=""; Method[] methods=inface.getMethods(); for(Method m:methods) { methodStr+="@Override"+rt+ "public void "+m.getName()+"()"+rt+"{" + rt + "   try {"+rt+ "  Method md="+inface.getName()+".class.getMethod(\""+m.getName()+"\");"+rt+ "h.invoke(this,md);"+rt+ "   } catch(Exception e){e.printStackTrace();}"+rt+ "}"; } String src="package test;"+rt+ "import java.lang.reflect.Method;"+rt+ "public class ServiceImpl2 implements "+inface.getName()+ rt+ "{"+rt+ "public ServiceImpl2(InvocationHandle h)"+rt+ "{"+rt+ "this.h = h;"+rt+ "}"+rt+ " test.InvocationHandle h;"+rt+ methodStr+ "}"; String fileName="d:/src/test/ServiceImpl2.java"; //compile
            compile(src, fileName); //load into memory and create instance Object m = loadMemory(h); return m; } private static void compile(String src, String fileName) throws IOException { File f=new File(fileName); FileWriter fileWriter=new FileWriter(f); fileWriter.write(src); fileWriter.flush(); fileWriter.close(); //获取此平台提供的Java编译器 JavaCompiler compiler=ToolProvider.getSystemJavaCompiler(); //获取一个标准文件管理器实现的新实例 StandardJavaFileManager fileManager=compiler.getStandardFileManager(null,null, null); //获取表示给定文件的文件对象 Iterable units=fileManager.getJavaFileObjects(fileName); //使用给定组件和参数创建编译任务的 future CompilationTask t=compiler.getTask(null, fileManager, null, null, null, units); //执行此编译任务
            t.call();     fileManager.close(); } private static Object loadMemory(InvocationHandle h) throws MalformedURLException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { URL[] urls=new URL[] {new URL("file:/"+"d:/src/")}; //从路径d:/src/加载类和资源 URLClassLoader ul=new URLClassLoader(urls); Class c=ul.loadClass("test.ServiceImpl2"); //返回Class对象所表示的类的指定公共构造方法。     Constructor ctr=c.getConstructor(InvocationHandle.class); //使用此 Constructor对象ctr表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例 Object m = ctr.newInstance(h); return m; } } 五、总结1、所谓的动态代理就是这样一种class,它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后改 class就宣称它实现了这些interface,但是其实它不会替你作实质性的工作,而是根据你在生成实例时提供的参数handler(即 InvocationHandler接口的实现类),由这个Handler来接管实际的工作。2、Proxy的设计使得它只能支持interface的代理,Java的继承机制注定了动态代理类无法实现对class的动态代理,因为多继承在Java中本质上就行不通。

转载于:https://www.cnblogs.com/Free-Thinker/p/10405492.html

Java动态代理的实现机制相关推荐

  1. Java 动态代理机制分析及扩展--转

    http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/#icomments http://www.ibm.com/developerworks/c ...

  2. Java 动态代理机制分析及扩展,第 1 部分

    引言 Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执 ...

  3. Java 动态代理机制分析及扩展

    简介: 本文通过分析 Java 动态代理的机制和特点,解读动态代理类的源代码,并且模拟推演了动态代理类的可能实现,向读者阐述了一个完整的 Java 动态代理运作过程,希望能帮助读者加深对 Java 动 ...

  4. Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)

    class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出 ...

  5. JVM插桩之四:Java动态代理机制的对比(JDK和CGLIB,Javassist,ASM)

    一.class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件, ...

  6. Java 动态代理机制详解

    在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...

  7. Java动态代理机制详解

    class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出 ...

  8. Java动态代理的实现

    动态代理作为代理模式的一种扩展形式,广泛应用于框架(尤其是基于AOP的框架)的设计与开发,本文将通过实例来讲解Java动态代理的实现过程. 友情提示:本文略有难度,读者需具备代理模式相关基础知识,. ...

  9. JAVA 动态代理学习记录

    打算用JAVA实现一个简单的RPC框架,看完RPC参考代码之后,感觉RPC的实现主要用到了两个方面的JAVA知识:网络通信和动态代理.因此,先补补动态代理的知识.---多看看代码中写的注释 参考:Ja ...

  10. Java动态代理的应用

    先看一下代理模式,这个应该是设计模式中最简单的一个了,类图 代理模式最大的特点就是代理类和实际业务类实现同一个接口(或继承同一父类),代理对象持有一个实际对象的引用,外部调用时操作的是代理对象,而在代 ...

最新文章

  1. 深度学习中的图像分割:方法和应用
  2. iphone通讯录批量删除_iPhone通讯录删除了如何恢复?用对方法快速找回,亲测有效!_...
  3. GitHub 私有仓库完全免费面向团队提供
  4. linux 查看网络 wait
  5. cocos2dx-3 addImageAsync陷阱
  6. 郫都区计算机老师周俊老师,教师节,带你走进郫都教师背后的故事
  7. 2019年第一批重点作品版权保护预警名单公布 都是贺岁档
  8. createprocess 系统找不到指定的文件_告别文件混乱和找不到,文件管理的新思路...
  9. iOS平台上的音视频即时通讯应用开发
  10. 物质为何能在虚空粒子海中存在
  11. 分析mrp主要应用范围_MRP软件行业现状调研分析及发展趋势预测报告(2020)
  12. Python视觉深度学习系列教程 第二卷 第3章 理解rank-1rank-5精度
  13. 春风吹又生(1年工作经验感悟)
  14. 电视机未来会成为家庭交互中心?
  15. JAVA中Iterator转List三种方法
  16. 开始学ASP.NET了~·~得发奋啊……
  17. 2022年最新版Spring专项面试突击
  18. pagerank算法详解
  19. libstdc++.so.6库环境配置
  20. 基于51单片机智能家居监控系统设计仿真(proteus仿真+源码+报告)

热门文章

  1. PopupWindow点击空白区域消失
  2. mac下chrome浏览器设置ajax跨域调试
  3. 洛谷 P2048 BZOJ 2006 [NOI2010]超级钢琴
  4. ListView添加图片文字项
  5. vue.js--遇到的一些错误
  6. kernel编译速度提高
  7. Pidgin cannot connect to MSN: the certificate chain presented is invalid
  8. oracle服务器配置及优化
  9. MySQL中文参考手册--8.MySQL教程--8.3 常用查询的例子
  10. 案例分享:巧用工具提升无源码系统的性能和稳定性