纯手写JDK动态代理【深入了解Proxy类底层实现】

一、JDK动态代理原理分析

1、在使用jdk动态代理的时候,必须要实现InvocationHandler接口

public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 被代理对象,目标对象
     */
    private Object target;

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

    /**
     *
     * @param proxy 代理类对象
     * @param method 被代理执行的方法
     * @param args 目标方法参数
     * @return 防止执行的返回值
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("日志收集开始。。。");
        Object result = method.invoke(target, args);
        System.out.println("日志收集结束。。。");
        return result;
    }

    /**
     * 生成代理类对象
     * @param <T>
     * @return
     */
    public <T> T getProxy(){
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces() , this);
    }
}

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

二、纯手写JDK动态代理原理分析

  1. 创建代理类$Proxy0源代码文件实现被代理的接口。
public final class $Proxy0 extends java.lang.reflect.Proxy implements com.xwhy.service.OrderService {
  1. 使用JavaCompiler技术编译该 P r o x y 0 文 件 获 取 到 Proxy0文件获取到 Proxy0Proxy0.class
  2. 使用ClassLoader将该$Proxy0.class加入到当前JVM内存中。

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

三、纯手写JDK动态代理V1.0

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

public interface MyExtJdkInvocationHandler {
    /**
     *
     * @param proxy 代理类
     * @param method 被代理执行的方法
     * @param args 参数
     * @return
     * @throws Throwable
     */
    Object invoke(Object proxy, Method method,Object[] args) throws Throwable;
}

2.写一个MyJdkInvocationHandler实现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);
        System.out.println("纯手写jdk动态代理开始。。。");
        return result;
    }
}

service层

public interface OrderService {
    void order() throws Throwable;
}
public class OrderServiceImpl implements OrderService {
    public void order() {
        System.out.println("执行订单操作。。。");
    }
}

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

public class $Proxy0 implements OrderService {
    private MyExtJdkInvocationHandler handler;

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

    public void order() throws Throwable {
        Method orderMethod = OrderService.class.getMethod("order", new Class[]{});
        this.handler.invoke(this, orderMethod, null);
    }
}

4、测试

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

5、打印结果

纯手写jdk动态代理开始。。。
执行订单操作。。。
纯手写jdk动态代理开始。。。

四、纯手写JDK动态代理V2.0

我们只需要将V1.0中的$Proxy0类变成动态生成就可以了。

1.看Proxy怎么实现的newProxyInstance
newProxyInstance

2.实现自己的MyProxy

package com.xwhy;

import com.xwhy.ext.proxy.JavaClassLoader;
import com.xwhy.ext.proxy.MyExtJdkInvocationHandler;
import com.xwhy.service.OrderService;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;


/**
 * @description: ${description}
 * @author: Mark
 * @create: 2019-05-26 16:02:40
 **/
public class MyProxy {
    private static String rt = "\r\t";

    public static Object newProxyInstance(JavaClassLoader javaClassLoader,
                                          Class<?> classInfo,
                                          MyExtJdkInvocationHandler h) {
        try {

            //1、拼接代理类源代码
            Method[] methods = classInfo.getMethods();
            String proxyClass = "package com.xwhy.ext.proxy;" + rt
                    + "import java.lang.reflect.Method;" + rt
                    + "import com.xwhy.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 = "/Users/mark/$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

package com.xwhy.ext.proxy;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class JavaClassLoader extends ClassLoader {

    private File classPathFile;

    public JavaClassLoader(){
//        String classPath=JavaClassLoader.class.getResource("").getPath();
        String classPath="/Users/mark";
        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;
    }
}

4.测试

public class TestClient {
    public static void main(String[] args) throws Throwable {
        OrderService orderService = (OrderService) MyProxy.newProxyInstance(new JavaClassLoader(),OrderService.class, new MyJdkInvocationHandler(new OrderServiceImpl()));
        orderService.order();
    }
}

生成的$Proxy0.class文件如下:
文件
运行时生成的$Proxy0.java文件如下:

package com.xwhy;

import java.lang.reflect.Method;

import com.xwhy.ext.proxy.MyExtJdkInvocationHandler;

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

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

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

}

5.运行结果

纯手写jdk动态代理开始。。。
执行订单操作。。。
纯手写jdk动态代理开始。。。

五、JDK动态代理Proxy.newProxyInstance源码分析

public class Proxy implements java.io.Serializable {
    
    //【3】缓存,这里有个ProxyClassFactory,代理类工厂初始化
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
    
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        //【4】将源代码编译成class文件,帮助我们初始化,创建class文件
        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                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");
                }
                /*
                 * 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.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }
			
            //代理类的包名,可以自定义
            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }
			
            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            //使用原子类自增保证代理类的名称自增的线程安全问题
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
            //将代理类的原代理生成字节码文件
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                //【5】使用ClassLoader将代理类的class文件加载到内存中
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }
    
    /**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        //【2】获取代理类,写入到本地文件
        return proxyClassCache.get(loader, interfaces);
    }
    
	public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
    {
       	...
        //【1】创建代理类java源代码,写入到硬盘
        Class<?> cl = getProxyClass0(loader, intfs);
        ...
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值