当前位置: 首页 > 编程日记 > 正文

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

相关文章:

20162313苑洪铭 第一周作业

20162313苑洪铭 20016-2017-2 《程序设计与数据结构》第1周学习总结 教材学习内容总结 本周观看教材绪论 主要在教我建立一个简单的java程序 内容是林肯的名言 虽然看起来很简单 但是实际上问题重重 总而言之 这一周全是在出现故障的 教材学习中的问题和解决过程 教材学习好像并…

测试驱动开发 测试前移_测试驱动的开发可能看起来是工作的两倍-但无论如何您都应该这样做...

测试驱动开发 测试前移by Navdeep Singh通过Navdeep Singh 测试驱动的开发可能看起来是工作的两倍-但无论如何您都应该这样做 (Test-driven development might seem like twice the work — but you should do it anyway) Isn’t Test Driven Development (TDD) twice the wor…

3 OC 属性和方法

1 OC 的属性的生成 interface Student:NSObject {publicint _no;int _age;}property (nonatomic,assign)int height;end 当我们使用property 的时候&#xff0c;那么系统会自动的在其内部生成个属性 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.c…

ios绘图时的坐标处理

在iOS中&#xff0c;进行绘图操作时&#xff0c;一般主要是在UIView:drawRect中调用 UIGraphicsBeginImageContextWithOptions等一系列函数&#xff0c;有时候直接画图就行&#xff0c;比如UIImage的drawRect等&#xff0c;有时候需要进行稍微复杂的操作&#xff0c;比如颜色混…

mongoDB数据库操作工具库

/* Mongodb的数据库工具类 */ var client require(mongodb).MongoClient;function MongoUtil() { this.url"mongodb://localhost:27017/storage";//在本地新建数据库storage&#xff0c;此后插入的数据都在storage中 }MongoUtil.prototype.connectfunction(callback…

开源许可证 如何工作_开源许可证的工作方式以及如何将其添加到您的项目中...

开源许可证 如何工作by Radu Raicea由Radu Raicea 开源许可证的工作方式以及如何将其添加到您的项目中 (How open source licenses work and how to add them to your projects) Recently, there was some exciting news for developers around the world. Facebook changed t…

通过API文档查询Math类的方法,打印出近似圆,只要给定不同半径,圆的大小就会随之发生改变...

package question;import java.util.Scanner; import java.lang.Math;public class MathTest {/*** 未搞懂* param args*/public static void main(String[] args) {// TODO Auto-generated method stubSystem.out.println("请输入圆的半径:");Scanner in new Scanne…

4 OC 中的内存分配以及内存对齐

目录 一 OC 中的内存分配 一 OC 中的内存分配 student 结构体明明是20&#xff1f;为什么是24个字节&#xff0c;因为结构体会按照本身成员变量最大的内存进行对齐&#xff0c;最大成员变量是8个字节&#xff0c;因此就是8的倍数&#xff0c;24个字节。 class_getInstanc…

JDE函数--GetUDC(B函数)

GetUDC使用方式&#xff1a; 转载于:https://www.cnblogs.com/GYoungBean/p/4117965.html

k8s crd构建方法_告诉您正在构建没人想要的东西的8种方法(以及处理方法)

k8s crd构建方法by Geoffrey Bourne杰弗里伯恩(Geoffrey Bourne) 告诉您正在构建没人想要的东西的8种方法(以及处理方法) (8 ways to tell you’re building something nobody wants (and what to do about it)) Building something users want is hard — damn hard. They ar…

iOS开发 - 线程与进程的认识与理解

进程&#xff1a; 进程是指在系统中正在运行的一个应用程序&#xff0c;比如同时打开微信和Xcode&#xff0c;系统会分别启动2个进程;每个进程之间是独立的&#xff0c;每个进程均运行在其专用且受保护的内存空间内;线程&#xff1a; 一个进程要想执行任务&#xff0c;必须得有…

Winform开发中常见界面的DevExpress处理操作

我们在开发Winform程序的时候&#xff0c;需要经常性的对界面的一些控件进行初始化&#xff0c;或者经常简单的封装&#xff0c;以方便我们在界面设计过程中反复使用。本文主要介绍在我的一些项目中经常性的界面处理操作和代码&#xff0c;以便为大家开发的时候提供必要的参考。…

5 OC 中的三种对象

目录 OC 中对象的分类 一 instance 对象 二 类对象 三 元类对象 总结: OC 中对象的分类 instance 对象 类对象 元类对象 一 instance 对象 内存中包含哪些信息 isa 指针 其他成员的变量Student *stu1 [[Student alloc]init]; 以上的stu1 就是实例对象 二 类对象 以…

travis ci_如何使用Travis CI和GitHub进行Web开发工作流程

travis ciby Vijayabharathi Balasubramanian通过Vijayabharathi Balasubramanian 如何使用Travis CI和GitHub进行Web开发工作流程 (How to use Travis CI and GitHub for your web development workflow’s heavy lifting) It’s common to hack together apps on CodePen wh…

android.view.ViewRoot$CalledFromWrongThreadException的解决办法

android 是不允许子线程直接更新UI的&#xff0c;如果一定要在子线程直接更新UI就会出现android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.大概意思就是说 只有原来创建找个视图hierarchy的…

6 OC中 isa 和 superclass 的总结

目录 一 关于isa 和 superclass 的总结 二 为什么基类的metaclass 的superclass 指向的是基类的类 三 isa 的细节问题 总结如下&#xff1a; instance 的isa 指向是classclass 的isa 指向是metaclassmetaclass 的isa指向是基类的imetaclassclass 的superclass 指向的是父类…

opencv下指定文件夹下的图片灰度化(图片的读取与保存)-------简单记录

对于此功能其实很简单&#xff1a;主要是在c方面的字母数字的拼接问题存在一定的问题。C数字字母拼接问题&#xff1a; 1 #include <fstream> 2 #include <string> 3 #include <iostream> 4 #include "highgui.h" 5 #include <cv.h> 6 #…

css菜单缓慢滑动_如何使用HTML,CSS和JavaScript构建滑动菜单栏

css菜单缓慢滑动by Supriya Shashivasan由Supriya Shashivasan 如何使用HTML&#xff0c;CSS和JavaScript构建滑动菜单栏 (How to build a sliding menu bar using HTML, CSS and JavaScript) A menu is what you look for when you land at a website. It has options and gi…

素数环问题---深度搜索遍历

1264: 素数环 时间限制: 1 Sec 内存限制: 128 MB提交: 29 解决: 8[提交][状态][讨论版]题目描述 有一个长度为n的环形序列由1,2,3,...,n组成&#xff0c;环中相邻两个整数和均为素数。你需要找到所有满足条件的环。输入 输入n表示环的长度&#xff08;n<16&#xff09;输出…

android之Notification通知

我们在用手机的时候&#xff0c;如果来了短信&#xff0c;而我们没有点击查看的话&#xff0c;是不是在手机的最上边的状态栏里有一个短信的小图标提示啊&#xff1f;你是不是也想实现这种功能呢&#xff1f;今天的Notification就是解决这个问题的。 package cn.com.chenzheng_…

7 OC 中class 类的结构

目录 一 OC 中class 的结构 https://opensource.apple.com/tarballs/objc4/ 在最新的objc源码中 化繁就简来看的话 是以下结构 struct objc_class : objc_object {objc_class(const objc_class&) delete;objc_class(objc_class&&) delete;void operator(con…

apple id无法创建_我们如何使用Apple的学习框架来创建我们的第一个应用程序

apple id无法创建by Jonata Corra由JonataCorra 我们如何使用Apple的学习框架来创建我们的第一个应用程序 (How we used Apple’s learning framework to create our first app) After one month of work, my team and I finished the first version of Echo, our tracker iOS…

个人作业1:小学四则运算——基于控制台

a.需求分析&#xff1a; 自动生成小学四则运算题目的命令行 “软件”&#xff0c;满足以下需求&#xff1a; 除了整数以外&#xff0c;还要支持真分数的四则运算&#xff0c;真分数的运算&#xff0c;例如&#xff1a;1/6 1/8 7/24运算符为 , −, , 并且要求能处理用户…

getchar返回int类型

#include <stdio.h> /* copy input to output; 2nd version */main(){int c;c getchar();while(c ! EOF){putchar(c);c getchar();}} 直觉告诉我getchar返回值应该是char类型的&#xff0c;这个地方为什么不能用char类型来存储getchar()的返回值呢&#xff1f; 其实文中…

8 iOS中KVO 的本质

前言本质 Automatic key-value observing is implemented using a technique called isa-swizzling 这计划的意思就是 自动的键值观察的实现基于 isa-swizzling 原理 1.KVO是基于runtime机制实现的 2.当某个类的属性对象第一次被观察时&#xff0c;系统就会在运行期动态地创…

完成工作表-使用Google Spreadsheets作为数据后端

by Gilad Dayagi通过吉拉德达亚吉 完成工作表-使用Google Spreadsheets作为数据后端 (Get Sheet Done — using Google Spreadsheets as your data backend) If you want to rapidly prototype your next web apps, try using Google Spreadsheets as your data backend.如果您…

BIEE-CSS样式大全

字体属性&#xff1a;(font) 大小 {font-size: x-large;}(特大) xx-small;(极小) 一般中文用不到&#xff0c;只要用数值就可以&#xff0c;单位&#xff1a;PX、PD 样式 {font-style: oblique;}(偏斜体) italic;(斜体) normal;(正常) 行高 {line-height: normal;}(正常) 单位&…

基于verilog的FPGA编程经验总结(XILINX ISE工具)

1.用ISE仿真的时候.所用变量一定要初始化. ISE默认初始量为"XXXXX", 而Quarters是默认为"00000"的, 其实实际上, 下到FPGA里后也是默认为0的,只是可以说ISE严谨得令人DT吧.比如说用一个累加器, result ABresult ,必须保证在某一刻A, B, result都为定值时,…

6 OC 中的isa 指针

目录 一 isa 指针 二 类对象中的superclass 一 isa 指针 isa 指针 &#xff0c;OC 中的对象都是有的 如下图所示&#xff0c;实例对象isa 指针指向 类对象&#xff0c;类对象的isa 指针指向 元类对象 二 类对象中的superclass superclass 有什么用呢&#xff1f; 比如说创…

btf-raft共识算法_了解Raft共识算法:学术文章摘要

btf-raft共识算法by Shubheksha通过Shubheksha 了解Raft共识算法&#xff1a;学术文章摘要 (Understanding the Raft consensus algorithm: an academic article summary) This post summarizes the Raft consensus algorithm presented in the paper In Search of An Underst…