JAVA JDK動態代理原理詳解

Java JDK動態代理原理詳解:

參考文章:https://blog.csdn.net/qq_31859365/article/details/82902349

動態代理介紹

Java動態代理與靜態代理相對,靜態代理是在編譯期就已經確定代理類和真實累的關係,並且聲稱代理類的,二動態代理是在運行期利用jvm的反射機制聲稱代理類,這裏是直接生成類的字節碼,然後通過類加載器將字節碼文件加載到Java虛擬機並執行的一種技術。現在主流的Java動態代理有兩種實現方式:一種是jdk自帶的,就是我們所說的jdk動態代理,一種是開源社區的開源項目CGLIB,本文主要講jdk代理的實現原理;
jdk動態代理的實現是在運行時,根據一組接口定義,使用Proxy、InvocationHandler等工具生成一個代理類和代理類實例;
如果還理解不了什麼是動態代理,請看這裏:Java代理模式詳解

JDK動態代理相關API

  • Java 編譯API:
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
  • InvocationHandler(接口):
    該接口是proxy代理實例的調用處理程序實現的一個接口,每個proxy代理實例都有一個關聯的調用處理程序;在代理實例調用方法時,方法調用被編髮分配到調用處理程序的invoke方法;
  • proxy類:
    該類就是用來創建一個代理對象的類,它提供了很多方法,但是我們最常用的是newProxyInstance方法;該方法有三個參數,具體參數介紹如下:
    1)、CLassLoader loader:一個ClassLoader對象,定義了由那個classloader對象生成的代理類進行加載;
    2)、Class<?> interfaces:一個interfaces對象數組,表示我們將要給我們的代理獨享提供一組什麼樣的接口,如果我們提供了這樣的一個接口對象數組,那麼也就是聲明瞭代理類實現了這些接口,代理類就可以調用接口中聲明的所有方法;
    3)、InvocationHandler h:一個InvocationHandler對象,表示的是當動態代理對象調用方法的時候會關聯到哪一個InvocationHandler對象上,並最終由其調用;

JDK動態代理過程

  • 定義一個接口:Movable,接口中有方法:move();
  • 定義一個方法調用接口InvocationHandler;
  • 定義一個方法調用接口實現類,用於調用真實對象的方法的同時添加自己的業務邏輯代碼;
  • 實現接口的目標類(它要被代理);
  • 定義一個處理器TimeHandler,重載構造方法,重寫invoke函數,添加自己的處理邏輯;
  • 代理邏輯實現過程編寫:
    1)、定義方法字符串,用於保存方法信息;
    2)、接口方法獲取;
    3)、生成實現類的對象信息
    4)、動態生成 $Proxy.java文件;
    5)、加載生成的 $Proxy.class字節碼指令到內存中;
    6)、返回生成對象的引用;

代碼詳解

  • 定義一個接口:
//定義一個接口;
public interface Moveable {
    void move();
}
  • 實現接口的目標類
package com.example.springbootdemotest.proxy.principle;

//實現接口的目標類
public class Tank implements Moveable
{
    @Override
    public void move() {
        System.out.println("Tank Move...");
    }
}

  • 定義一個方法調用接口
package com.example.springbootdemotest.proxy.principle;

import java.lang.reflect.Method;

//定義一個方法調用接口
public interface InvocationHandler {
     void invoke(Object o, Method m);
}

  • 定義一個調用接口方法實現類
package com.example.springbootdemotest.proxy.principle;

import java.lang.reflect.Method;

public class TimeHandler implements InvocationHandler{

    private Object target;
    public TimeHandler(Object target){
        this.target = target;
    }
    @Override
    public void invoke(Object o, Method m) {
    	//此處自己編寫執行真實對象方法“前”業務邏輯
        long start = System.currentTimeMillis();
        System.out.println("StartTime:"+start);
        System.out.println(o.getClass().getName());
        try {
            m.invoke(target);
        }catch (Exception e){
            e.printStackTrace();
        }
        //此處自己編寫執行真實對象方法“後”業務邏輯
        long end = System.currentTimeMillis();
        System.out.println("EndTime:"+end);
        System.out.println("time:"+(end-start));
    }
}
  • 編寫接口實現過程:
package com.example.springbootdemotest.proxy.principle;


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;
import java.net.URL;
import java.net.URLClassLoader;

//代理對象,
public class Proxy {
    public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception {
        //1、定義方法字符串,用於保存方法信息;
        String methodStr = "";
        String rt = "\r\n";
        //2、接口方法獲取;
        Method[] methods = infce.getMethods();
        for (Method m : methods) {
            //生成方法信息;
            methodStr += "@Override" + rt +
                    " public void "+ m.getName()+"(){"+rt+
                    "   try{" + rt +
                    "       Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt + //找到具體方法
                    "       h.invoke(this,md);" + rt +
                    "   }catch(Exception e){e.printStackTrace();}" + rt +
                    "}";
        }
        //3、生成實現類的對象信息
        String src ="package com.example.springbootdemotest.proxy.principle;"+ rt +
                "import java.lang.reflect.Method;" +rt +
                "public class $Proxy1 implements "+ infce.getName() +"{" +rt +
                "   public $Proxy1(InvocationHandler h){" +rt +
                "       this.h = h;" +rt +
                "   }" +rt +
                "   com.example.springbootdemotest.proxy.principle.InvocationHandler h;" +rt +
                    methodStr+
                "}";

        //4、動態生成$Proxy.java文件;
        //D:\software\gitRespository\springbootdemo\src\main\java\com\example\springbootdemotest\proxy\principle\$Proxy1.java
        String fileName = System.getProperty("user.dir")+"/springbootdemo-test/src/main/java/com/example/springbootdemotest/proxy/principle/$Proxy1.java";
        File f = new File(fileName);
        FileWriter fw = new FileWriter(f);
        fw.write(src);
        fw.flush();
        fw.close();

        //5、執行動態編譯過程;
        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();

        //6、加載生成的$Proxy.class到內存中;
        //D:\software\gitRespository\springbootdemo\springbootdemo-test\src\main\java\com\example\springbootdemotest\proxy\clibPackage\MainClass.java
		//URL[] urls = new URL[]{new URL("file:"+System.getProperty("user.dir")+"/src/main/java")};
        URL[] urls = new URL[]{new URL("file:"+"D:/software/gitRespository/springbootdemo/springbootdemo-test/src/main/java")};

        URLClassLoader ul = new URLClassLoader(urls);
        Class c = ul.loadClass("com.example.springbootdemotest.proxy.principle.$Proxy1");
        System.out.println(c);
        Constructor ctr = c.getConstructor(InvocationHandler.class);
        Object m = ctr.newInstance(h);
        //m.move();
        return m;
    }

    public static void main(String[] args) throws Exception{
        Tank tank = new Tank();
        InvocationHandler h = new TimeHandler(tank);
        Moveable m = (Moveable) Proxy.newProxyInstance(Moveable.class,h);
        m.move();
    }
}
  • 執行結果如下:
class com.example.springbootdemotest.proxy.principle.$Proxy1
StartTime:1576233182481
com.example.springbootdemotest.proxy.principle.$Proxy1
Tank Move...
EndTime:1576233182482
time:1

Process finished with exit code 0

- 上述過程完整模擬jdk動態代理過程,且相關類名同jdk動態代理類名相同,如有疑問,請看:Java代理模式詳解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章