jdk动态代理源码学习
最近用到了java的动态代理,虽然会用,但不了解他具体是怎么实现,抽空看看了看他的源码。
说到Java的动态代理就不能不说到代理模式,动态代理也就是多了一个’动态’两字,在《大话设计模式》中不是有这句话吗?“反射,反射程序员的快乐”,这里也不例外,他在底层也是使用了反射来创建对象。
一、 为了让我们更加明白的了解动态代理,我们先来复习一下代理模式吧(没有学过的,也得假装复习是复习呀,不然掉面)。
public interface BookManager {void addBook();
}
1 package com.test; 2 3 public class Library implements BookManager { 4 5 public void addBook() { 6 // TODO Auto-generated method stub 7 System.out.println("增加图书。。。。。"); 8 } 9 10 }
1 package com.test; 2 3 public class Agent implements BookManager { 4 private BookManager library; 5 6 public BookManager getLibrary() { 7 return library; 8 } 9 10 public void setLibrary(BookManager library) { 11 this.library = library; 12 } 13 14 public void addBook() { 15 // TODO Auto-generated method stub 16 System.out.println("添加图书之前"); 17 library.addBook(); 18 System.out.println("添加图书之后"); 19 } 20 21 }
测试代码
package com.test;public class BookTest {public static void main(String[] args) {BookManager library = new Library();Agent agent = new Agent();agent.setLibrary(library);agent.addBook();}
}执行结果为
添加图书之前
增加图书。。。。。
添加图书之后
从这里可以看到在代理模式中要求是都实现了相同的接口,所以这样的代码,移植性不强,所以催生出动态代理。动态代理不要求,必须实现相同的接口,减少了代码量。
先上代码
public interface BookFacade {public void addBook();
}
public class BookFacadeImpl implements BookFacade,system {public void addBook() {// TODO Auto-generated method stubSystem.out.println("增加图书方法。。。"); }public void doSys() {// TODO Auto-generated method stubSystem.out.println("dsfsdfsd");}}
public class BookFacadeProxy implements InvocationHandler {private Object target; /** * 绑定委托对象并返回一个代理类 * @param target * @return */ public Object bind(Object target) { this.target = target; //取得代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷) } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result=null; System.out.println("事物开始"); //执行方法 System.out.println("ClassLoad"+proxy.getClass().getSimpleName());result=method.invoke(target, args); System.out.println("事物结束"); return result; }
}
public class TestProxy {public static void main(String[] args) {BookFacadeProxy proxy = new BookFacadeProxy(); BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl()); bookProxy.addBook(); ProxyGeneratorUtils.writeProxyClassToHardDisk("F:/$Proxy11.class");}
}
我先声明后面这段代码我盗用了其他网友的成果。
先说明用法,再来解释为什么会是这样的呢?
第一步 你必须得声明一个接口,而且在目标类必须实现这个接口,不然你使用动态代理是不会成功的。
第二步 需要实现 InvocationHandler 接口,创建一个代理类,这个代理类里需要重写invoke方法,在这个方法里,需要写上多被代理对象的调用method.invoke(target, args),如果不知道method.invoke()是做什么用的,可以去看看反射就明白了。参数有两个一个是被代理的对象,第二个就是调用该方法的参数
第三步 需要获得代理对象,可以通过Object Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
方法来生成一个代理对象。
具体的第一个是使用那一个类加载器加载,第二个就是需要绑定的接口,第三个就是持有处理的对象。
测试方法安装你正常调用相应的方法就行。
测试类执行结果如下
事物开始
增加图书方法。。。
事物结束
现在我们知道用法了,按照我们一般考虑问题的思路是,
生成的代理对象,能调用接口的的方法,肯定是实现了接口这是我们的猜想一,这个涉及到代理对象的生成。
第二个调用addBook方法,能执行,invoke方法,他是怎么调用的呢。
那么我们现在就进入源码吧,先来看看对象是怎么生成的。
Returns an instance of a proxy class for the specified interfacesthat dispatches method invocations to the specified invocationhandler.看看注释就明白了,返回一个代理类的对象,而且还是实现了你进来接口的类public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{if (h == null) {throw new NullPointerException();}/** 生成一个代理类*/Class<?> cl = getProxyClass(loader, interfaces);/** 通过反射用构造方法生成一个对象*/try {Constructor cons = cl.getConstructor(constructorParams);return 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());}}
上面大家可以看到,生成了一个对象返回去了,这个对象就是我们所说的代理对象,那么我们可以看看这个class 是怎么生成?
public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)throws IllegalArgumentException{验证接口的长度if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}这个class 对象就是我们被返回对象的定义Class<?> proxyClass = null;/* collect interface names to use as key for proxy class cache */String[] interfaceNames = new String[interfaces.length];// for detecting duplicatesSet<Class<?>> interfaceSet = new HashSet<>();for (int i = 0; i < interfaces.length; i++) {String interfaceName = interfaces[i].getName();Class<?> interfaceClass = null;try {interfaceClass = Class.forName(interfaceName, false, loader);} catch (ClassNotFoundException e) {}if (interfaceClass != interfaces[i]) {throw new IllegalArgumentException(interfaces[i] + " is not visible from class loader");}/** Verify that the Class object actually represents an* interface.*/if (!interfaceClass.isInterface()) {throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface");}/** Verify that this interface is not a duplicate.*/if (interfaceSet.contains(interfaceClass)) {throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());}interfaceSet.add(interfaceClass);interfaceNames[i] = interfaceName;}....... 中间省略N多 生成一个代理class文件,这个就是我们要被返回的class 对象/** Generate the specified proxy class.*/byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);try {proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) {/*
这样我们就知道这个代理对象是怎么生成的。
接下来我们第2个问题就是这个invoke 方法是什么时候调用的。
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
从这个方法我们可以看出来,他可以生成一个二进制的文件,那么我们把这个文件写到本地就可以看到这个文件了。
public final class $Proxy110 extends Proxyimplements BookFacade
{private static Method m1;private static Method m3;private static Method m0;private static Method m2;public $Proxy110(InvocationHandler paramInvocationHandler)throws {super(paramInvocationHandler);}public final boolean equals(Object paramObject)throws {try{return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();}catch (Error|RuntimeException localError){throw localError;}catch (Throwable localThrowable){throw new UndeclaredThrowableException(localThrowable);}}public final void addBook()throws {try{this.h.invoke(this, m3, null);return;}catch (Error|RuntimeException localError){throw localError;}catch (Throwable localThrowable){throw new UndeclaredThrowableException(localThrowable);}}
这个是生成
转载于:https://www.cnblogs.com/2014----/p/4050886.html
jdk动态代理源码学习相关推荐
- jdk动态代理源码分析(一)---代理的定义
最近在看rpc的实现原理,发现大部分通用的rpc框架在实现远程调用的时候,都是通过java动态代理封装好了通信细节,让用户可以像调用本地服务一样调用远程服务.但是关于java动态代理有两个问题想不通: ...
- 深入剖析JDK动态代理源码实现
动态代理.静态代理优缺点 优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性.这是代理的共有优点.动态代理只有在用到被代理对象的时候才会对被代理类进行类加载. 而静态代理在编译器就已经开始占内存 ...
- JDK动态代理源码解析
分析版本jdk1.8 在分析jdk动态代理之前,先来了解java WeakReference弱引用的使用.运行期创建目标对象的代理非常耗时,使用缓存来存储生成的代理类显得尤为重要.jdk动态代理使用弱 ...
- 动态代理及JDK动态代理源码分析
1.为什么要动态代理 现在有这样一个需求:在聊天系统中,把每一个所说的话记录到日志文件里面,初学者可能是这样来设计 在speak方法中调用log方法,记录到数据库.这样设计有明显的不足:1.log方法 ...
- Java动态代理源码详解
一.概述 前言:本文除了讲解JDK动态代理及CGLIB动态代理实例和应用外,还会讲解JDK动态代理源码实现过程以及自己写一手个JDK动态代理等. 动态代理在很多底层框架中都会用得到,比如在Sp ...
- javabean反射改字段内容_BAT程序员编写:深入理解 Java 反射和动态代理源码分析...
什么是反射 反射(Reflection)是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性. 通过反射机制,可以在运行时访问 Java ...
- 动态代理源码分析,实现自己的动态代理
什么是代理 增强一个对象的功能 买火车票,app就是一个代理,他代理了火车站,小区当中的代售窗口 java当中如何实现代理 java实现的代理的两种办法 代理的名词 代理对象 增强后的对象 目标对象 ...
- Android动态代理源码分析
前言 前面我们简单介绍了代理模式的使用Android设计模式详解之代理模式,我们也明白了动态代理模式的重要性,那动态代理究竟是如何实现将方法交给InvocationHandler.invoke执行的呢 ...
- jkd动态代理源码分析
代理对象的生成方法是: Proxy.newProxyInstance(...),进入这个方法内部,一步一步往下走会发现会调用 ProxyGenerator.generateProxyClass(),这 ...
最新文章
- MySQL 为什么表的数据删除一般,表文件大小不变?
- kafka的SASL的适用业务场景
- android rxbus 一个页面监听,Android RxBus的使用
- 配置源码管理工具(2)
- 前端学习(1716):前端系列javascript之页面配置下
- node 安装 webpack
- rocketmq存储结构_阿里专家分享内部绝密RocketMQ核心原理与最佳实践笔记
- Python基础(map/reduce)
- @程序员,一文掌握 Web 应用中的图片优化技巧!
- python识别复杂验证码2020_Python识别验证码!学会这步,百分之60的网站你基本都能识别了!...
- 开课吧Java课堂:特殊的字符串如何操作,字符串如何连接
- 朋友们给《Java程序员,上班那点事儿》的赠言
- 部分贴片电阻标准阻值表(印字阻值对照)
- 自检zabbix健康脚本
- React TypeScript react+ts 包下载
- 资源管理Placement部署(Nova)
- 【英语】maintext2-Emotional Mastery译文
- Min3D测试-在Android里面载入MMD模型-快速3D模型显示测试-By黑月君
- MAC 常见的终端指令
- HTML添加css文件和js文件
热门文章
- 以太坊:比特币 + 无限可能
- sql avg函数使用格式_SQL AVG-SQL平均函数用语法示例解释
- 让我们讨论一下变量,以及为什么要在JavaScript中使用它们。
- Apache服务器部署(2)
- 什么是python第三方库
- .net内存回收与Dispose﹐Close﹐Finalize方法
- 使用Novell.Directory.Ldap.NETStandard在.NET Core中验证AD域账号
- 统一客服消息返回错误:{errcode:43004,errmsg:require subscribe hint: [9Vv08633952]}
- 《从零开始学Swift》学习笔记(Day 47)——final关键字
- openresty 前端开发入门五之Mysql篇