专栏首页须臾之余深入Proxy底层源码——实现自己的JDK动态代理

深入Proxy底层源码——实现自己的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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spring5.0源码深度解析之SpringBean声明事务底层实现原理

    将InfrastructureAdvisorAutoProxyCreator注入到IOC容器中:

    须臾之余
  • Spring5.0源码深度解析之Spring核心注解

    1.为什么要使用@Import注解?Import注解的主要作用的将外部的jar包注入到springioc容器中

    须臾之余
  • 可重入的独占锁——ReentrantLock源码分析

    lock()获取锁,其实就是把state从0变成n(重入锁可以累加)。实际调用的是sync的lock方法,分公平和非公平。

    须臾之余
  • DSL 系列(1) - 扩展点的论述与实现

    DSL 全称为 domain-specific language(领域特定语言),本系列应当会很长,其中包含些许不成熟的想法,欢迎私信指正。

    捷义
  • Spring - FactoryBean配置Bean(7)

    桑鱼
  • ​Java 注解详解

    注解是在Java 1.5之后引入的,为的是可以直接在代码中编写元信息。在注解发布之前,如果想要描述代码只能将其写入到其他文件中,例如xml。 注解可以应用于包,...

    代码拾遗
  • 万万没想到一个xxl-job源码分析,竟然能引发这么多血案!(上)

    executor端通过上文中xxlRpcProviderFactory.invokeService(xxlRpcRequest);函数执行本地暴露的服务方法

    用户2032165
  • 掌握@ControllerAdvice配合RequestBodyAdvice/ResponseBodyAdvice使用,让你的选择不仅仅只有拦截器【享学Spring MVC】

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    YourBatman
  • SpringBoot 应用篇之从 0 到 1 实现一个自定义 Bean 注册器

    我们知道在 spring 中可以通过@Component,@Service, @Repository 装饰一个类,通过自动扫描注册为 bean;也可以通过在配置...

    一灰灰blog
  • xBIM 实战03 使用WPF技术实现IFC模型的加载与浏览

      WPF应用程序在底层使用 DirectX ,无论设计复杂的3D图形(这是 DirectX 的特长所在)还是绘制简单的按钮与文本,所有绘图工作都是通过 Dir...

    张传宁老师

扫码关注云+社区

领取腾讯云代金券