文档章节

深入Proxy底层源码——实现自己的JDK动态代理

须臾之余
 须臾之余
发布于 05/21 16:00
字数 1599
阅读 67
收藏 1
JDK

写在前面:设计模式源于生活,而又高于生活!

JDK动态代理原理分析

  1. 在使用jdk动态代理的时候,必须要实现InvocationHandler接口;invoke方法中该三个参数分别表示为: 代理对象、被代理执行的方法、参数
public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 被代理类对象 目标代理对象
     */
    private Object target;

    public JdkInvocationHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(">>>jdk打印订单日志开始:proxy:"+proxy.getClass().toString());
        Object reuslt = method.invoke(target, args);// java的反射机制执行方法 执行目标对象的方法
        System.out.println(">>>jdk打印订单日志结束");
        return reuslt;
    }

    /**
     * 使用jdk动态代理创建代理类
     *
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}

2.使用jdk动态代理获取代理类对象(JDK自动生成代理类) $Proxy0.class,使用反编译工具

纯手写动态代理原理分析

  1. 创建代理类$Proxy0源代码文件实现被代理的接口
public final class $Proxy0 extends java.lang.reflect.Proxy implements com.xuyu.service.OrderService {

    2.使用JavaCompiler技术编译该$Proxy0文件获取到$Proxy0.class

    3. 使用ClassLoader将该$Proxy0.class加入到当前JVM内存中

ClassLoader 顾名思义就是类加载器,ClassLoader 作用:负责将 Class 加载到 JVM 中,审查每个类由谁加载(父优先的等级加载机制),将 Class字节码 重新解析成 JVM 统一要求的对象格式

纯手写v1.0版本jdk动态代理

1.自定义MyExtJdkInvocationHandler接口 ——相当于InvocationHandler

/**
 * @title: MyExtJdkInvocationHandler 
 */
public interface MyExtJdkInvocationHandler {
    /**
     * @param proxy  代理类
     * @param method 目标方法
     * @param args   参数
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
}

2.写一个MyJdkInvocationHandler实现MyJdkInvocationHandler接口

/**
 * @title: MyJdkInvocationHandler
 */
public class MyJdkInvocationHandler implements MyExtJdkInvocationHandler {
    /**
     * 目标对象 被代理的类 真实访问的类的对象
     */
    private Object target;

    public MyJdkInvocationHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("<<<<<<<<<纯手写jdk动态代理日志开始>>>>>>>>>>");
        Object result = method.invoke(target, args);// 使用java的反射执行
        System.out.println("<<<<<<<<纯手写jdk动态代理结束>>>>>>>>>>");
        return result;
    }
}

service层

public interface OrderService {

    public void order() throws Throwable;
}
/**
 * @title: OrderServiceImpl
 */
public class OrderServiceImpl implements OrderService {

    public void order() {
        System.out.println("数据库订单执行操作");
    }
}

3.对比反编译$Proxy0.class来写$Proxy0

4.简单测试

public class Test001 {
    public static void main(String[] args) throws Throwable {
        OrderService  orderService = new $Proxy0(new MyJdkInvocationHandler(new OrderServiceImpl()));
        orderService.order();
    }
}

5.控制台输出结果

<<<<<<<<<纯手写jdk动态代理日志开始>>>>>>>>>>
数据库订单执行操作
<<<<<<<<纯手写jdk动态代理结束>>>>>>>>>>

纯手写v2.0版本jdk动态代理

1.先看源码Proxy怎么实现的

public class Proxy implements java.io.Serializable {
    ....
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
    ....
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        //【2】获取代理类,写入到到本地文件中..
        return proxyClassCache.get(loader, interfaces);
    }
    ....
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        private static final String proxyClassNamePrefix = "$Proxy";
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
        //【3】将源代码编译成class文件,帮助我们初始化,创建class文件
        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }
         
            //####代理的包名,可自定义####
            String proxyPkg = null;  
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
            for (Class<?> intf : interfaces) {
               。。。。
            }
            if (proxyPkg == null) {
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
            //####原子类,自增,保证线程安全,原子类计数代理类proxy0,proxy1,proxy2....####
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            //####创建Java源代码,转化为字节码文件####
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                //【4】使用classLoader 加载到内存中,代理类class文件加载到内存####
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
    }
    ....
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        。。。。
        //【1】创建代理类java源码文件,写入到硬盘中..
        Class<?> cl = getProxyClass0(loader, intfs);
       
        。。。。

1.总结相关要点

1.创建代理类java源码文件,写入到硬盘中..
2. 写入到到本地文件中..
3. 将源代码编译成class文件
4.使用classLoader 加载到内存中..

2.模仿源码写代码

/**
 * @title: MyProxy
 */
public class MyProxy {
    static String rt = "\r\t";

    public static Object newProxyInstance(JavaClassLoader javaClassLoader,
                                          Class<?> classInfo,
                                          MyExtJdkInvocationHandler h) {
        //1.拼接代理类的源代码
        try {

            // 1.创建代理类java源码文件,写入到硬盘中..
            Method[] methods = classInfo.getMethods();
            String proxyClass = "package com.xuyu.ext.proxy;" + rt
                    + "import java.lang.reflect.Method;" + rt
                    + "import com.xuyu.ext.proxy.MyExtJdkInvocationHandler;" + rt
                    + "public class $Proxy0 implements " + classInfo.getName() + "{" + rt
                    + "MyExtJdkInvocationHandler h;" + rt
                    + "public $Proxy0(MyExtJdkInvocationHandler h)" + "{" + rt
                    + "this.h= h;" + rt + "}"
                    + getMethodString(methods, classInfo) + rt + "}";
            // 2. 写入到到本地文件中..
            String filename = "d:/code/$Proxy0.java";
            File f = new File(filename);
            FileWriter fw = new FileWriter(f);
            fw.write(proxyClass);
            fw.flush();
            fw.close();
            // 3. 将源代码编译成class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(filename);
            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
            fileMgr.close();
            // 4.使用classLoader 加载到内存中..
            Class<?> $Proxy0 = javaClassLoader.findClass("$Proxy0");
            // 5.指明初始化有参数构造函数
            Constructor<?> constructor = $Proxy0.getConstructor(MyExtJdkInvocationHandler.class);
            Object o = constructor.newInstance(h);
            return o;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public static String getMethodString(Method[] methods, Class intf) {
        String proxyMe = "";
        for (Method method : methods) {
            proxyMe += "public void " + method.getName() + "() throws Throwable {" + rt
                    + "Method md= " + intf.getName() + ".class.getMethod(\"" + method.getName()
                    + "\",new Class[]{});" + rt
                    + "this.h.invoke(this,md,null);" + rt + "}" + rt;

        }
        return proxyMe;
    }

    public static void main(String[] args) {
        newProxyInstance(null, OrderService.class, null);
    }

}

3.看源码怎么实现ClassLoader类加载器

4.写出自己的JavaClassLoader 类加载器

public class JavaClassLoader extends ClassLoader {

    private File classPathFile;

    public JavaClassLoader(){
        String classPath="D:\\code";
        this.classPathFile=new File(classPath);
    }

    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        String className= JavaClassLoader.class.getPackage().getName()+"."+name;
        if(classPathFile!=null){
          File classFile=new File(classPathFile,name.replaceAll("\\.","/")+".class");
          if(classFile.exists()){
              FileInputStream in=null;
              ByteArrayOutputStream out=null;
              try {
                  in=new FileInputStream(classFile);
                  out=new ByteArrayOutputStream();
                  byte[] buff=new byte[1024];
                  int len;
                  while ((len=in.read(buff))!=-1){
                     out.write(buff,0,len);
                  }
                  return defineClass(className,out.toByteArray(),0,out.size());
              }catch (Exception e){
                  e.printStackTrace();
              }finally {
                  if(in!=null){
                      try {
                          in.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
                  if(out!=null){
                      try {
                          out.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
        }
        return null;
    }

5.测试类

/**
 * @title: Test001
 */
public class Test001 {
    public static void main(String[] args) throws Throwable {

        OrderService orderService = (OrderService) MyProxy.newProxyInstance(new JavaClassLoader(),
                OrderService.class, new MyJdkInvocationHandler(new OrderServiceImpl()));
        orderService.order();
    }
}

6.运行程序生成的$Proxy0.java文件内容

package com.xuyu;

import com.xuyu.ext.proxy.MyExtJdkInvocationHandler;

import java.lang.reflect.Method;


public class $Proxy0 implements com.xuyu.service.OrderService {
    MyExtJdkInvocationHandler h;

    public $Proxy0(MyExtJdkInvocationHandler h) {
        this.h = h;
    }

    public void order() throws Throwable {
        Method md = com.xuyu.service.OrderService.class.getMethod("order",
                new Class[] {  });
        this.h.invoke(this, md, null);
    }
}

7.控制台输出结果

<<<<<<<<<纯手写jdk动态代理日志开始>>>>>>>>>>
数据库订单执行操作
<<<<<<<<纯手写jdk动态代理结束>>>>>>>>>>

版权@须臾之余https://my.oschina.net/u/3995125

本文参考:

蚂蚁课堂:www.mayikt.com

© 著作权归作者所有

须臾之余
粉丝 124
博文 64
码字总数 164381
作品 0
吉安
程序员
私信 提问
深入理解Java动态代理及手动实现

前言 文章目录如下,便于快速索引 一、什么叫代理? 二、什么叫动态代理? 三、动态代理有什么优势? 四、动态代理的JDK实现原理 4.1核心类/接口 4.2 代理类$Proxy0解析 4.3 动态代理的经典使...

HOT_POT
2018/07/28
89
0
Spring AOP 源码分析——创建代理对象

1.简介 与筛选合适的通知器相比,创建代理对象的过程则要简单不少,本文所分析的源码不过100行,相对比较简单。在接下里的章节中,我将会首先向大家介绍一些背景知识,然后再去分析源码。那下...

代码屠夫18
2018/06/21
465
0
java jdk与cglib动态代理模式的认识和实现

1.使用java jdk Proxy实现动态代理,该原理是反射机制。 建立一个普通的接口 package com.tester.cls.design.mode; public interface IUser { public String getName(); public void setNam......

IamOkay
2014/10/30
116
0
java实现动态代理的方式有几种?

AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函...

丑的想整容
2018/01/13
156
0
Java动态代理的两种实现方法

AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函...

Zhang_Java
2016/11/17
189
0

没有更多内容

加载失败,请刷新页面

加载更多

CSS--列表

一、列表标识项 list-style-type none:去掉标识项 disc:默认实心圆 circle:空心圆 squire:矩形 二、列表项图片 list-style-img: 取值:url(路径) 三、列表项位置 list-style-position:...

wytao1995
今天
4
0
linux 命令-文本比较comm、diff、patch

本文原创首发于公众号:编程三分钟 今天学了三个文本比较的命令分享给大家。 comm comm 命令比较相同的文本 $ cat charabc$ cat chardiffadc 比如,我有两个文件char和chardiff如上,...

编程三分钟
今天
7
0
QML教程

https://blog.csdn.net/qq_40194498/article/category/7580030 https://blog.csdn.net/LaineGates/article/details/50887765...

shzwork
今天
5
0
HA Cluster之5

对于使用heartbeat v2版的CRM配置的集群信息都是保存在一个名为cib.xml的配置文件中,存放在/var/lib/heartbeat/crm/下。CIB:Cluster Information Base,由于xml文件配置不是那么方便,所以...

lhdzw
今天
6
0
玩转Redis-Redis基础数据结构及核心命令

  《玩转Redis》系列文章主要讲述Redis的基础及中高级应用,文章基于Redis5.0.4+。本文主要讲述Redis的数据结构String,《玩转Redis-Redis基础数据结构及核心命令》相关操作命令为方便对比...

zxiaofan666
今天
11
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部