最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

手動(dòng)模擬JDK動(dòng)態(tài)代理的方法

 更新時(shí)間:2020年11月25日 15:52:12   作者:賜我白日夢(mèng)  
這篇文章主要介紹了手動(dòng)模擬JDK動(dòng)態(tài)代理的方法,幫助大家更好的了解和學(xué)習(xí)Java 代理的相關(guān)知識(shí),感興趣的朋友可以了解下

為哪些方法代理?

實(shí)現(xiàn)自己動(dòng)態(tài)代理,首先需要關(guān)注的點(diǎn)就是,代理對(duì)象需要為哪些方法代理? 原生JDK的動(dòng)態(tài)代理的實(shí)現(xiàn)是往上抽象出一層接口,讓目標(biāo)對(duì)象和代理對(duì)象都實(shí)現(xiàn)這個(gè)接口,怎么把接口的信息告訴jdk原生的動(dòng)態(tài)代理呢? 如下代碼所示,Proxy.newProxyInstance()方法的第二個(gè)參數(shù)將接口的信息傳遞了進(jìn)去第一個(gè)參數(shù)的傳遞進(jìn)去一個(gè)類加載器,在jdk的底層用它對(duì)比對(duì)象是否是同一個(gè),標(biāo)準(zhǔn)就是相同對(duì)象的類加載器是同一個(gè)

ServiceInterface) Proxy.newProxyInstance(service.getClass().getClassLoader()
        , new Class[]{ServiceInterface.class}, new InvocationHandler() {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置通知");
        method.invoke(finalService,args);
        System.out.println("后置通知");
        return proxy;
      }
    });

我們也效仿它的做法. 代碼如下:

public class Test {
  public static void main(String[] args) {
    IndexDao indexDao = new IndexDao();
    Dao dao =(Dao) ProxyUtil.newInstance(Dao.class,new MyInvocationHandlerImpl(indexDao));
    assert dao != null;
    System.out.println(dao.say("changwu"));
  }
}

拿到了接口的Class對(duì)象后,通過(guò)反射就得知了接口中有哪些方法描述對(duì)象Method,獲取到的所有的方法,這些方法就是我們需要增強(qiáng)的方法

如何將增強(qiáng)的邏輯動(dòng)態(tài)的傳遞進(jìn)來(lái)呢?

JDK的做法是通過(guò)InvocationHandler的第三個(gè)參數(shù)完成,他是個(gè)接口,里面只有一個(gè)抽象方法如下: 可以看到它里面有三個(gè)入?yún)?分別是 代理對(duì)象,被代理對(duì)象的方法,被代理對(duì)象的方法的參數(shù)

  public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;
}

當(dāng)我們使用jdk的動(dòng)態(tài)代理時(shí),就是通過(guò)這個(gè)重寫這個(gè)鉤子函數(shù),將邏輯動(dòng)態(tài)的傳遞進(jìn)去,并且可以選擇在適當(dāng)?shù)牡胤阶屇繕?biāo)方法執(zhí)行

InvocationHandler接口必須存在必要性1:

為什么不傳遞進(jìn)去Method,而是傳遞進(jìn)去InvocationHandler對(duì)象呢? 很顯然,我們的初衷是借助ProxyUtil工具類完成對(duì)代理對(duì)象的拼串封裝,然后讓這個(gè)代理對(duì)象去執(zhí)行method.invoke(), 然而事與愿違,傳遞進(jìn)來(lái)的Method對(duì)象的確可以被ProxyUtil使用,調(diào)用method.invoke(), 但是我們的代理對(duì)象不能使用它,因?yàn)榇韺?duì)象在這個(gè)ProxyUtil還以一堆等待拼接字符串, ProxyUtil的作用只能是往代理對(duì)象上疊加字符串,卻不能直接傳遞給它一個(gè)對(duì)象,所以只能傳遞一個(gè)對(duì)象進(jìn)來(lái),然后通過(guò)反射獲取到這個(gè)對(duì)象的實(shí)例,繼而有可能實(shí)現(xiàn)method.invoke()

InvocationHandler接口必須存在必要性2:

通過(guò)這個(gè)接口的規(guī)范,我們可以直接得知回調(diào)方法的名字就是invoke()所以說(shuō),在拼接字符串完成對(duì)代理對(duì)象的拼接時(shí),可以直接寫死它

思路

我們需要通過(guò)上面的ProxyUtil.newInstance(Dao.class,new MyInvocationHandlerImpl(indexDao))方法完成如下幾件事

  1. 根據(jù)入?yún)⑽恢玫男畔?提取我們需要的信息,如包名,方法名,等等
  2. 根據(jù)我們提取的信息通過(guò)字符串的拼接完成一個(gè)全新的java的拼接
  3. 這個(gè)java類就是我們的代理對(duì)象
  4. 拼接好的java類是一個(gè)String字符串,我們將它寫入磁盤取名XXX.java
  5. 通過(guò)ProxyUtil使用類加載器,將XXX.java讀取JVM中,形成Class對(duì)象
  6. 通過(guò)Class對(duì)象反射出我們需要的代理對(duì)象
  7. ProxyUtil的實(shí)現(xiàn)如下:
public static Object newInstance(Class targetInf, MyInvocationHandler invocationHandler) {

  Method methods[] = targetInf.getDeclaredMethods();
  String line = "\n";
  String tab = "\t";
  String infName = targetInf.getSimpleName();
  String content = "";
  String packageContent = "package com.myproxy;" + line;
  //  導(dǎo)包,全部導(dǎo)入接口層面,換成具體的實(shí)現(xiàn)類就會(huì)報(bào)錯(cuò)
  //  
  String importContent = "import " + targetInf.getName() + ";" + line
              + "import com.changwu.代理技術(shù).模擬jdk實(shí)現(xiàn)動(dòng)態(tài)代理.MyInvocationHandler;" + line
              + "import java.lang.reflect.Method;" + line
              + "import java.lang.Exception;" + line;

  String clazzFirstLineContent = "public class $Proxy implements " + infName +"{"+ line;
  String filedContent = tab + "private MyInvocationHandler handler;"+ line;
  String constructorContent = tab + "public $Proxy (MyInvocationHandler handler){" + line
      + tab + tab + "this.handler =handler;"
      + line + tab + "}" + line;
  String methodContent = "";
  // 遍歷它的全部方法,接口出現(xiàn)的全部方法進(jìn)行增強(qiáng)
  for (Method method : methods) {
    String returnTypeName = method.getReturnType().getSimpleName();     method.getReturnType().getSimpleName());

    String methodName = method.getName();
    Class<?>[] parameterTypes = method.getParameterTypes();

    // 參數(shù)的.class
    String paramsClass = "";
    for (Class<?> parameterType : parameterTypes) {
      paramsClass+= parameterType.getName()+",";
    }

    String[] split = paramsClass.split(",");

    //方法參數(shù)的類型數(shù)組 Sting.class String.class
    String argsContent = "";
    String paramsContent = "";
    int flag = 0;
    for (Class arg : parameterTypes) {
      // 獲取方法名
      String temp = arg.getSimpleName();
      argsContent += temp + " p" + flag + ",";
      paramsContent += "p" + flag + ",";
      flag++;
    }
    // 去掉方法參數(shù)中最后面多出來(lái)的,
    if (argsContent.length() > 0) {
      argsContent = argsContent.substring(0, argsContent.lastIndexOf(",") - 1);
      paramsContent = paramsContent.substring(0, paramsContent.lastIndexOf(",") - 1);
    }
    methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsContent + ") {" + line
        + tab + tab+"Method method = null;"+line
        + tab + tab+"String [] args0 = null;"+line
        + tab + tab+"Class<?> [] args1= null;"+line

        // invoke入?yún)⑹荕ethod對(duì)象,而不是上面的字符串,所以的得通過(guò)反射創(chuàng)建出Method對(duì)象
        + tab + tab+"try{"+line
        // 反射得到參數(shù)的類型數(shù)組
         + tab + tab + tab + "args0 = \""+paramsClass+"\".split(\",\");"+line
         + tab + tab + tab + "args1 = new Class[args0.length];"+line
         + tab + tab + tab + "for (int i=0;i<args0.length;i++) {"+line
         + tab + tab + tab + "  args1[i]=Class.forName(args0[i]);"+line
         + tab + tab + tab + "}"+line
        // 反射目標(biāo)方法
        + tab + tab + tab + "method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\",args1);"+line
        + tab + tab+"}catch (Exception e){"+line
        + tab + tab+ tab+"e.printStackTrace();"+line
        + tab + tab+"}"+line
        + tab + tab + "return ("+returnTypeName+") this.handler.invoke(method,\"暫時(shí)不知道的方法\");" + line; //
         methodContent+= tab + "}"+line;
  }

  content = packageContent + importContent + clazzFirstLineContent + filedContent + constructorContent + methodContent + "}";

  File file = new File("d:\\com\\myproxy\\$Proxy.java");
  try {
    if (!file.exists()) {
      file.createNewFile();
    }

    FileWriter fw = new FileWriter(file);
    fw.write(content);
    fw.flush();
    fw.close();

    // 將生成的.java的文件編譯成 .class文件
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
    Iterable units = fileMgr.getJavaFileObjects(file);
    JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
    t.call();
    fileMgr.close();

    // 使用類加載器將.class文件加載進(jìn)jvm
    // 因?yàn)楫a(chǎn)生的.class不在我們的工程當(dāng)中
    URL[] urls = new URL[]{new URL("file:D:\\\\")};
    URLClassLoader urlClassLoader = new URLClassLoader(urls);
    Class clazz = urlClassLoader.loadClass("com.myproxy.$Proxy");
    return clazz.getConstructor(MyInvocationHandler.class).newInstance(invocationHandler);
  } catch (Exception e) {
    e.printStackTrace();
  }
    return null;
}
}

運(yùn)行的效果:

package com.myproxy;
import com.changwu.myproxy.pro.Dao;
import com.changwu.myproxy.pro.MyInvocationHandler;
import java.lang.reflect.Method;
import java.lang.Exception;
public class $Proxy implements Dao{
	private MyInvocationHandler handler;
	public $Proxy (MyInvocationHandler handler){
		this.handler =handler;
	}
	public String say(String p) {
		Method method = null;
		String [] args0 = null;
		Class<?> [] args1= null;
		try{
			args0 = "java.lang.String,".split(",");
			args1 = new Class[args0.length];
			for (int i=0;i<args0.length;i++) {
			  args1[i]=Class.forName(args0[i]);
			}
			method = Class.forName("com.changwu.myproxy.pro.Dao").getDeclaredMethod("say",args1);
		}catch (Exception e){
			e.printStackTrace();
		}
		return (String) this.handler.invoke(method,"暫時(shí)不知道的方法");
	}
}

解讀

通過(guò)newInstance()用戶獲取到的代理對(duì)象就像上面的代理一樣,這個(gè)過(guò)程是在java代碼運(yùn)行時(shí)生成的,但是直接看他的結(jié)果和靜態(tài)代理差不錯(cuò),這時(shí)用戶再去調(diào)用代理對(duì)象的say(), 實(shí)際上就是在執(zhí)行用戶傳遞進(jìn)去的InvocationHandeler里面的invoke方法, 但是亮點(diǎn)是我們把目標(biāo)方法的描述對(duì)象Method同時(shí)給他傳遞進(jìn)去了,讓用戶可以執(zhí)行目標(biāo)方法+增強(qiáng)的邏輯

當(dāng)通過(guò)反射區(qū)執(zhí)行Method對(duì)象的invoke()方法時(shí),指定的哪個(gè)對(duì)象的當(dāng)前方法呢? 這個(gè)參數(shù)其實(shí)是我們手動(dòng)傳遞進(jìn)去的代理對(duì)象代碼如下

public class MyInvocationHandlerImpl implements MyInvocationHandler {
  private Object obj;
  public MyInvocationHandlerImpl(Object obj) {
    this.obj = obj;
  }
  @Override
  public Object invoke(Method method, Object[] args) {
    System.out.println("前置通知");
    try {
      method.invoke(obj,args);
    } catch (Exception e) {
      e.printStackTrace();
    } 
    System.out.println("后置通知");
    return null;
  }
}

作者: 賜我白日夢(mèng)

出處:https://www.cnblogs.com/ZhuChangwu/p/11648911.html

以上就是手動(dòng)模擬JDK動(dòng)態(tài)代理的方法的詳細(xì)內(nèi)容,更多關(guān)于模擬jdk動(dòng)態(tài)代理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring?Boot項(xiàng)目如何使用Maven打包并帶上依賴

    Spring?Boot項(xiàng)目如何使用Maven打包并帶上依賴

    在這篇博客中,介紹如何使用Maven將Spring?Boot項(xiàng)目及其依賴項(xiàng)打包成一個(gè)可執(zhí)行的jar文件。我們將使用Spring?Boot的spring-boot-maven-plugin插件來(lái)完成這個(gè)任務(wù),感興趣的朋友跟隨小編一起看看吧
    2023-06-06
  • Java實(shí)現(xiàn)的爬蟲抓取圖片并保存操作示例

    Java實(shí)現(xiàn)的爬蟲抓取圖片并保存操作示例

    這篇文章主要介紹了Java實(shí)現(xiàn)的爬蟲抓取圖片并保存操作,涉及Java針對(duì)頁(yè)面URL訪問、獲取、字符串匹配、文件下載等相關(guān)操作技巧,需要的朋友可以參考下
    2018-08-08
  • Java 判斷線程池所有任務(wù)是否執(zhí)行完畢的操作

    Java 判斷線程池所有任務(wù)是否執(zhí)行完畢的操作

    這篇文章主要介紹了Java 判斷線程池所有任務(wù)是否執(zhí)行完畢的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-08-08
  • Java中StopWatch工具類的用法詳解

    Java中StopWatch工具類的用法詳解

    stopWatch 是org.springframework.util 包下的一個(gè)工具類,使用它可直觀的輸出代碼執(zhí)行耗時(shí),以及執(zhí)行時(shí)間百分比,下面就跟隨小編一起來(lái)看看它的具體用法吧
    2024-12-12
  • 在java8中使用流區(qū)分質(zhì)數(shù)與非質(zhì)數(shù)詳解

    在java8中使用流區(qū)分質(zhì)數(shù)與非質(zhì)數(shù)詳解

    這篇文章主要介紹了在java8中使用流區(qū)分質(zhì)數(shù)與非質(zhì)數(shù)詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • 最長(zhǎng)重復(fù)子數(shù)組 findLength示例詳解

    最長(zhǎng)重復(fù)子數(shù)組 findLength示例詳解

    今天給大家分享一道比較常問的算法面試題,最長(zhǎng)重復(fù)子數(shù)組 findLength,文中給大家分享解題思路,結(jié)合示例代碼介紹的非常詳細(xì),需要的朋友參考下吧
    2023-08-08
  • Java多線程模式之Balking模式詳解

    Java多線程模式之Balking模式詳解

    這篇文章主要介紹了Java多線程模式之Balking模式,結(jié)合實(shí)例形式較為詳細(xì)的分析了Balking模式的原理、用法與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-06-06
  • Java中值類型和引用類型的比較與問題解決

    Java中值類型和引用類型的比較與問題解決

    這篇文章主要給大家介紹了關(guān)于Java中值類型和引用類型的比較與問題解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • java格式化數(shù)值成貨幣格式示例

    java格式化數(shù)值成貨幣格式示例

    這篇文章主要介紹了java格式化數(shù)值成貨幣格式示例,格式化一個(gè)數(shù)值,比如123456789.123,希望顯示成"$123,456,789.123",需要的朋友可以參考下
    2014-04-04
  • java 字符串截取的三種方法(推薦)

    java 字符串截取的三種方法(推薦)

    下面小編就為大家?guī)?lái)一篇java 字符串截取的三種方法(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-11-11

最新評(píng)論

微山县| 邹城市| 新泰市| 无极县| 肥东县| 宁晋县| 台湾省| 宝兴县| 泽库县| 巴彦县| 额尔古纳市| 谢通门县| 广丰县| 德安县| 四平市| 文安县| 天津市| 鲁甸县| 大埔区| 大冶市| 山阴县| 宣威市| 永州市| 沭阳县| 德保县| 新建县| 孟津县| 洛扎县| 贡觉县| 邛崃市| 闵行区| 大足县| 麦盖提县| 大姚县| 买车| 陆良县| 邮箱| 万载县| 布尔津县| 青冈县| 封丘县|