引言

项目结构如下图所示,maven项目

1、JDK动态代理

先来一段jdk动态代理的demo,

首先创建一个接口,Person

  1. package bean;
  2. public interface Person {
  3. public void eat();
  4. }

然后写一个实现类PersonImpl

  1. package bean;
  2. public class PersonImpl implements Person{
  3. @Override
  4. public void eat() {
  5. // TODO Auto-generated method stub
  6. System.out.println("time to eat ");
  7. }
  8. }

然后写个使用类PersonInvocationHandler

  1. package jdk;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. public class PersonInvocationHandler implements InvocationHandler {
  5. private Object obj;
  6. public PersonInvocationHandler(Object obj) {
  7. this.obj = obj;
  8. }
  9. @Override
  10. public Object invoke(Object proxy, Method method, Object[] args)
  11. throws Throwable {
  12. // TODO Auto-generated method stub
  13. System.out.println("before time to eat");
  14. method.invoke(obj, args);
  15. System.out.println("after time to eat");
  16. return null;
  17. }
  18. }

最后 再写个测试类

  1. package jdk;
  2. import java.io.FileNotFoundException;
  3. import java.io.FileOutputStream;
  4. import java.lang.reflect.Proxy;
  5. import sun.misc.ProxyGenerator;
  6. import bean.Person;
  7. import bean.PersonImpl;
  8. public class jdkTest {
  9. public static void main(String[] args) throws Exception {
  10. PersonInvocationHandler personInvocationHandler = new PersonInvocationHandler(
  11. new PersonImpl());
  12. Person personProxy = (Person) Proxy.newProxyInstance(
  13. PersonImpl.class.getClassLoader(),
  14. PersonImpl.class.getInterfaces(), personInvocationHandler);
  15. personProxy.eat();
  16. }
  17. }

输出如下

  1. before time to eat
  2. time to eat
  3. after time to eat

接下里我们不使用JDK的API,自己实现一套代理类

2、自定义动态代理

先上测试类的代码,如下图所示,共有(1)(2)(3)处不同

针对(1),我们有如下代码,先抄袭JDK的InvocationHandler,改个名字成为MyInvocationHandler

  1. package custom;
  2. import java.lang.reflect.Method;
  3. public interface MyInvocationHandler {
  4. public Object invoke(Object proxy, Method method, Object[] args)
  5. throws Throwable;
  6. }

编写一个JAVA类MyPersonInvocationHandler继承MyInvocationHandler,这段代码与PersonInvocationHandler的代码无异,如下所示

  1. package custom;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. public class MyPersonInvocationHandler implements MyInvocationHandler {
  5. private Object obj;
  6. public MyPersonInvocationHandler(Object obj) {
  7. this.obj = obj;
  8. }
  9. @Override
  10. public Object invoke(Object proxy, Method method, Object[] args)
  11. throws Throwable {
  12. // TODO Auto-generated method stub
  13. System.out.println("before time to eat");
  14. method.invoke(obj, args);
  15. System.out.println("after time to eat");
  16. return null;
  17. }
  18. }

针对(2),我们实现一个自己的代理生成类MyProxy,其生成java代理类的步骤分为以下5步

  1. 生成java源碼
  2. 將源码输出到java文件中
  3. 将java文件编译成class文件
  4. 将class加载进jvm
  5. 返回代理类对象

具体代码如下

  1. package custom;
  2. import java.io.File;
  3. import java.io.FileWriter;
  4. import java.lang.reflect.Constructor;
  5. import java.lang.reflect.Method;
  6. import javax.tools.JavaCompiler;
  7. import javax.tools.StandardJavaFileManager;
  8. import javax.tools.ToolProvider;
  9. public class MyProxy {
  10. public static final String ln = "\r\n";
  11. public static Object newProxyInstance(MyClassLoader myClassLoder,
  12. Class<?>[] interfaces, MyInvocationHandler h) {
  13. try{
  14. // 1 java源碼
  15. String src = generateSrc(interfaces);
  16. // 2 將源码输出到java文件中
  17. String filePath = MyProxy.class.getResource("").getPath();
  18. System.out.println(filePath);
  19. File f = new File(filePath + "$Proxy0.java");
  20. FileWriter fw = new FileWriter(f);
  21. fw.write(src);
  22. fw.flush();
  23. fw.close();
  24. //3、将java文件编译成class文件
  25. JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
  26. StandardJavaFileManager manage = compiler.getStandardFileManager(null,null,null);
  27. Iterable iterable = manage.getJavaFileObjects(f);
  28. JavaCompiler.CompilationTask task = compiler.getTask(null,manage,null,null,null,iterable);
  29. task.call();
  30. manage.close();
  31. //4、将class加载进jvm
  32. Class proxyClass=myClassLoder.findClass("$Proxy0");
  33. f.delete();
  34. //5、返回代理类对象
  35. Constructor constructor = proxyClass.getConstructor(MyInvocationHandler.class);
  36. return constructor.newInstance(h);
  37. }catch(Exception e){
  38. e.printStackTrace();
  39. }
  40. return null;
  41. }
  42. private static String generateSrc(Class<?>[] interfaces) {
  43. // TODO Auto-generated method stub
  44. StringBuffer sb = new StringBuffer();
  45. sb.append("package custom;" + ln);
  46. sb.append("import java.lang.reflect.Method;" + ln);
  47. sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
  48. sb.append("private MyInvocationHandler h;"+ln);
  49. sb.append("public $Proxy0(MyInvocationHandler h) { " + ln);
  50. sb.append("this.h = h;"+ln);
  51. sb.append("}" + ln);
  52. for (Method m : interfaces[0].getMethods()) {
  53. sb.append("public " + m.getReturnType().getName() + " "
  54. + m.getName() + "() {" + ln);
  55. sb.append("try{" + ln);
  56. sb.append("Method m = " + interfaces[0].getName()
  57. + ".class.getMethod(\"" + m.getName()
  58. + "\",new Class[]{});" + ln);
  59. sb.append("this.h.invoke(this,m,null);" + ln);
  60. sb.append("}catch(Throwable e){" + ln);
  61. sb.append("e.printStackTrace();" + ln);
  62. sb.append("}"+ln);
  63. sb.append("}"+ln);
  64. }
  65. sb.append("}" + ln);
  66. return sb.toString();
  67. }
  68. }

针对(3),我们继承ClassLoader,实现一套自己的类加载机制MyClassLoader,如下所示,

  1. package custom;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.IOException;
  6. public class MyClassLoader extends ClassLoader {
  7. private File classPathfile;
  8. public MyClassLoader() {
  9. String classpth = MyClassLoader.class.getResource("").getPath();
  10. classPathfile = new File(classpth);
  11. }
  12. @Override
  13. public Class<?> findClass(String name) throws ClassNotFoundException {
  14. String className = MyClassLoader.class.getPackage().getName() + "." +name;
  15. if (classPathfile != null) {
  16. File file = new File(classPathfile, name + ".class");
  17. FileInputStream fileInputStream = null;
  18. ByteArrayOutputStream outputStream = null;
  19. try{
  20. fileInputStream = new FileInputStream(file);
  21. outputStream = new ByteArrayOutputStream();
  22. byte[] buff = new byte[1024];
  23. int len;
  24. while((len=fileInputStream.read(buff))!=-1){
  25. outputStream.write(buff, 0, len);
  26. }
  27. return defineClass(className, outputStream.toByteArray(), 0, outputStream.size());
  28. }catch(Exception e){
  29. e.printStackTrace();
  30. }finally{
  31. if(null!=fileInputStream){
  32. try {
  33. fileInputStream.close();
  34. } catch (IOException e) {
  35. // TODO Auto-generated catch block
  36. e.printStackTrace();
  37. }
  38. }
  39. if(null!=outputStream){
  40. try {
  41. outputStream.close();
  42. } catch (IOException e) {
  43. // TODO Auto-generated catch block
  44. e.printStackTrace();
  45. }
  46. }
  47. }
  48. }
  49. return null;
  50. }
  51. }

最后测试类代码如下所示

  1. package custom;
  2. import java.lang.reflect.Proxy;
  3. import custom.MyPersonInvocationHandler;
  4. import bean.Person;
  5. import bean.PersonImpl;
  6. public class CustomTest {
  7. public static void main(String[] args) {
  8. MyPersonInvocationHandler personInvocationHandler = new MyPersonInvocationHandler(
  9. new PersonImpl());
  10. Person personProxy = (Person) MyProxy.newProxyInstance(
  11. new MyClassLoader(), PersonImpl.class.getInterfaces(),
  12. personInvocationHandler);
  13. personProxy.eat();
  14. }
  15. }

输出如何所示

  1. before time to eat
  2. time to eat
  3. after time to eat

至此,我们已完全实现了一套自定义的jdk动态代理类

【原创】自己动手实现JDK动态代理的更多相关文章

  1. 【原创】分布式之缓存击穿 【原创】自己动手实现静态资源服务器 【原创】自己动手实现JDK动态代理

    [原创]分布式之缓存击穿   什么是缓存击穿 在谈论缓存击穿之前,我们先来回忆下从缓存中加载数据的逻辑,如下图所示 因此,如果黑客每次故意查询一个在缓存内必然不存在的数据,导致每次请求都要去存储层去查 ...

  2. 动态代理学习(一)自己动手模拟JDK动态代理

    最近一直在学习Spring的源码,Spring底层大量使用了动态代理.所以花一些时间对动态代理的知识做一下总结. 我们自己动手模拟一个动态代理 对JDK动态代理的源码进行分析 文章目录 场景: 思路: ...

  3. 自己动手实现JDK动态代理

    出自:作者:孤独烟  http://rjzheng.cnblogs.com/ ------------------------------------------------------------- ...

  4. Java JDK 动态代理使用及实现原理分析

    转载:http://blog.csdn.net/jiankunking   一.什么是代理? 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理 ...

  5. 【原创】JDK动态代理,此次之后,永生难忘。

    动态代理,这个词在Java的世界里面经常被提起,尤其是对于部分(这里强调“部分”二字,因为有做了一两年就成大神的,实力强的令人发指,这类人无疑是非常懂动态代理这点小伎俩的)做了一两年新人来说,总是摸不 ...

  6. JDK动态代理

    一.基本概念 1.什么是代理? 在阐述JDK动态代理之前,我们很有必要先来弄明白代理的概念.代理这个词本身并不是计算机专用术语,它是生活中一个常用的概念.这里引用维基百科上的一句话对代理进行定义: A ...

  7. jdk动态代理与cglib代理、spring aop代理实现原理

    原创声明:本博客来源与本人另一博客[http://blog.csdn.net/liaohaojian/article/details/63683317]原创作品,绝非他处摘取 代理(proxy)的定义 ...

  8. jdk动态代理与cglib代理、spring aop代理实现原理解析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

  9. 何为代理?jdk动态代理与cglib代理、spring Aop代理原理浅析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

随机推荐

  1. delphi XE Datasnap SERVER 在windows 7 下为服务添加描述信息

    网上对服务添加描述信息的,多数是用注册表的方式,而注册表的方式,我在win7 下测试,不知为什么,总是不行,把执行的CMD以管理员模式开启,还是没加进去. 于是在网上查到下面的代码,保存供叁考,原文博 ...

  2. PHP之取得当前时间函数方法

    PHP之取得当前时间函数方法 PHP之取得当前时间函数方法文章提供了php的几种获取当前时间的函数,date,time等,同时告诉我如何解决时区问题.php教程取得当前时间函数文章提供了php的几种获 ...

  3. 警示:AL32UTF8字符集不是ZHS16GBK字符集的超集

    今天有一个客户向我咨询:数据库由ZHS16GBK字符集修改为AL32UTF8字符集,发现中文的数据中小部分出现乱码,客户认为AL32UTF8明明可以支持更多的文字,不应该出现这样的情况才对. 从现象看 ...

  4. Spring Framework 简介

    Spring Framework 依赖注入.事务管理.Web应用程序.数据访问.消息传递.测试和更多的核心支持. Tips: Spring 官网:https://spring.io/ spring f ...

  5. [jdoj1817]Drainage Ditches_网络流

    Drainage Ditches jdoj-1817 题目大意:网络流裸求最大流 注释:n(点数),m(边数)<=200. 想法:裸的网络流求最大流,用bfs+dfs,美其名曰dinic. 没有 ...

  6. Dynamics 365 for CRM: Sitemap站点图的可视化编辑功能

    Dynamics 365 for CRM 提供了Sitemap站点图的可视化编辑功能 在之前的所有版本中,我们只能通过从系统中导出站点图的XML进行编辑后再导入(容易出错),或使用第三方的Sitema ...

  7. beta冲刺 用户使用调查报告

    测评结果 一.使用体验 数据加载响应很快,页面切换丝滑流畅. UI有点偏暗,有些字被覆盖了. 页面布局过于居中,两侧空白范围较大. 总体功能完善. 二.登录.注册.忘记密码界面 管理员登录按钮太靠下, ...

  8. 数据结构基础——结构体struct及类型别名typedef的使用

    一.结构体的创建 在C语言中,实现数据结构的一种常用方法便是使用结构体(structure)其示例代码如下: struct stu { int num; char ch; }; struct表示创建结 ...

  9. day9

    Alpha冲刺Day9 一:站立式会议 今日安排: 经过为期5天的冲刺,基本完成企业人员模块的开发.因第三方机构与企业存在委托的关系.第三方人员对于风险的自查.风险列表的展示以及自查风险的统计展示(包 ...

  10. C语言--第四周作业

    一.题目7-1 计算分段函数[1] 1.代码 #include <stdio.h> int main () { float x,result; scanf("%f",& ...