深刻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,使用反編譯工具java

純手寫動態代理原理分析

  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接口app

/**
 * @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層ide

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怎麼實現的this

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

相關文章
相關標籤/搜索