成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

Java動(dòng)態(tài)代理深度解析

whinc / 1097人閱讀

摘要:動(dòng)態(tài)代理深度解析引言說(shuō)起動(dòng)態(tài)代理,很多人可能都沒(méi)有直接去使用過(guò)。因?yàn)榈膭?dòng)態(tài)代理只能代理接口,而不能代理原始的類(lèi)。接下來(lái)是真正壓軸的環(huán)節(jié),實(shí)現(xiàn)自己的動(dòng)態(tài)代理類(lèi)。

Java動(dòng)態(tài)代理深度解析 引言

說(shuō)起動(dòng)態(tài)代理,很多人可能都沒(méi)有直接去使用過(guò)。但是只要用過(guò)Spring,那動(dòng)態(tài)代理就是一個(gè)是個(gè)繞不過(guò)的坎,因?yàn)镾pring的核心特性之一AOP就是基于動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)的,那么什么情況下需要用到動(dòng)態(tài)代理呢?

場(chǎng)景

考慮這樣一個(gè)教師的接口:

public interface Teacher {
    void teach();
}

假設(shè)我們有一個(gè)TeacherChan的實(shí)現(xiàn)類(lèi),陳老師教的是攝影:

public class TeacherChan implements Teacher {

    @Override
    public void teach() {
        System.out.println("大家好,我是陳老師,我教大家攝影!");
    }
    
}

另外還有一個(gè)TeacherCang的實(shí)現(xiàn)類(lèi),蒼老師教的是生物:

public class TeacherCang implements Teacher {

    @Override
    public void teach() {
        System.out.println("大家好,我是蒼老師,我教大家生物!");
    }
    
}

不管是陳老師還是蒼老師,只要實(shí)現(xiàn)了Teacher這個(gè)接口,給我們傳道授業(yè)解惑,為了禮貌起見(jiàn),我們總應(yīng)該給人家問(wèn)聲好吧。而問(wèn)好這件事不需要老師主動(dòng)要求,可以交給代理來(lái)做,每次有老師來(lái)上課,代理自動(dòng)做了問(wèn)好這件事。而代理類(lèi)又分為靜態(tài)代理和動(dòng)態(tài)代理,靜態(tài)代理在編寫(xiě)代碼時(shí)已經(jīng)確定了要代理的類(lèi),只能代理單一的類(lèi)型,在此略過(guò),今天重點(diǎn)講動(dòng)態(tài)代理。

Java動(dòng)態(tài)代理

Java動(dòng)態(tài)代理創(chuàng)建代理類(lèi)的方法為:

Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

其中ClassLoader是用來(lái)定義代理類(lèi)的class文件的,用系統(tǒng)默認(rèn)的就好,interfaces是要代理的接口,InvocationHandler是用來(lái)實(shí)際執(zhí)行代理方法的接口,常用做法是實(shí)現(xiàn)該接口,并將需要代理的類(lèi)實(shí)例對(duì)象傳進(jìn)去。
實(shí)現(xiàn)自己的方法執(zhí)行器:

public class JdkDynamicProxy implements InvocationHandler {

    private Object proxied;

    public JdkDynamicProxy(Object object) {
        this.proxied = object;
    }

    /**
     * proxy為創(chuàng)建的代理類(lèi)實(shí)例,method是本次被代理的方法,args是方法的參數(shù)
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("-------------------老師好[by jdk動(dòng)態(tài)代理]-------------------");
        // 執(zhí)行代理方法
        Object obj = method.invoke(proxied, args);
        System.out.println("-------------------老師再見(jiàn)[by jdk動(dòng)態(tài)代理]-------------------");
        // 返回方法執(zhí)行結(jié)果
        return obj;
    }

}

創(chuàng)建代理類(lèi)對(duì)象并執(zhí)行:

// 代理陳老師
Teacher proxy1 = (Teacher) Proxy.newProxyInstance(Teacher.class.getClassLoader(),
        new Class[]{Teacher.class}, new JdkDynamicProxy(new TeacherChan()));
proxy1.teach();
// 代理蒼老師
Teacher proxy2 = (Teacher) Proxy.newProxyInstance(Teacher.class.getClassLoader(),
        new Class[]{Teacher.class}, new JdkDynamicProxy(new TeacherCang()));
proxy2.teach();

輸出結(jié)果:

-------------------老師好[by jdk動(dòng)態(tài)代理]-------------------
大家好,我是陳老師,我教大家攝影!
-------------------老師再見(jiàn)[by jdk動(dòng)態(tài)代理]-------------------
-------------------老師好[by jdk動(dòng)態(tài)代理]-------------------
大家好,我是蒼老師,我教大家生物!
-------------------老師再見(jiàn)[by jdk動(dòng)態(tài)代理]-------------------

實(shí)際上,Java會(huì)過(guò)濾掉接口所有final、native等方法,并為剩下的所有符合條件的方法生成代理方法。而且,熟悉Spring的朋友應(yīng)該知道,Spring的AOP機(jī)制的實(shí)現(xiàn)不僅使用了Java的動(dòng)態(tài)代理,而且還引入了CGLib。因?yàn)镴ava的動(dòng)態(tài)代理只能代理接口,而不能代理原始的類(lèi)。那么為什么Java不能代理類(lèi)呢,答案是Java的單繼承機(jī)制。

深入Java動(dòng)態(tài)代理的實(shí)現(xiàn)

Java的動(dòng)態(tài)代理是怎么實(shí)現(xiàn)的呢?其實(shí)很簡(jiǎn)單,就是運(yùn)行時(shí)生成一個(gè)代理類(lèi),該類(lèi)實(shí)現(xiàn)了需要代理的接口,并返回這個(gè)代理類(lèi)的實(shí)例對(duì)象給調(diào)用者。調(diào)試進(jìn)入Proxy.newProxyInstance()的方法內(nèi)部,可以看到在Proxy內(nèi)部生成class字節(jié)碼的方法:

// 生成的代理類(lèi)名前綴
private static final String proxyClassNamePrefix = "$Proxy";
// 生成的代理類(lèi)名序號(hào)
private static final AtomicLong nextUniqueNumber = new AtomicLong();

// 序號(hào)值加1
long num = nextUniqueNumber.getAndIncrement();
// 代理類(lèi)名:$ProxyN
String proxyName = proxyPkg + proxyClassNamePrefix + num;

...

// 生成代理類(lèi)的class字節(jié)碼
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

不難看出,生成的代理類(lèi)的類(lèi)名是$ProxyN的形式,所以我們經(jīng)常會(huì)看到$Proxy0這個(gè)類(lèi),就是動(dòng)態(tài)代理在運(yùn)行時(shí)生成的。

既然知道了動(dòng)態(tài)代理是怎么生成代理類(lèi)的了,那我們不妨把它生成的類(lèi)打印出來(lái)看看,到底里面是怎么實(shí)現(xiàn)的。

// 調(diào)用Java生成字節(jié)碼文件的方法
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
        "com.test.$proxy0.class", new Class[]{Teacher.class}, Modifier.FINAL);
// 輸出文件到本地
FileOutputStream out = new FileOutputStream(new File("/temp/$Proxy0.class"));
out.write(proxyClassFile);
out.flush();
out.close();

用java反編譯軟件打開(kāi)生成的$Proxy0.class文件,內(nèi)容如下:

package com.test.$proxy0;

import com.demos.java.basedemo.proxy.bean.Teacher;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class class extends Proxy
  implements Teacher
{
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;

  public class(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void teach()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m3 = Class.forName("com.demos.java.basedemo.proxy.bean.Teacher").getMethod("teach", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

可以看出,代理類(lèi)不僅代理了teach()這個(gè)方法,還代理了toString()、equals()和hashCode()方法,因?yàn)閖ava中所有的類(lèi)都是繼承Object類(lèi)的,所以自然有這些方法。其中還有一個(gè)地方要特別注意:生成的代理類(lèi)繼承了Proxy這個(gè)類(lèi),因此它只能通過(guò)實(shí)現(xiàn)接口來(lái)代理其他的類(lèi),現(xiàn)在知道為什么Java動(dòng)態(tài)代理只能代理接口了。

既然都講到這個(gè)份上了,當(dāng)然不能不繼續(xù)深入了。接下來(lái)是真正壓軸的環(huán)節(jié),實(shí)現(xiàn)自己的動(dòng)態(tài)代理類(lèi)。

手動(dòng)實(shí)現(xiàn)動(dòng)態(tài)代理

首先分析一下實(shí)現(xiàn)動(dòng)態(tài)代理需要的步驟:

1.拼接java類(lèi)實(shí)現(xiàn),并生成class字節(jié)碼
2.加載class字節(jié)碼到JVM
3.實(shí)現(xiàn)自己的InvocationHandler處理器
4.對(duì)外提供接口

既然知道了Java動(dòng)態(tài)代理的原理,我們不妨借鑒Java生成的class文件格式,同時(shí)去掉默認(rèn)繼承的Proxy,使得我們自己的動(dòng)態(tài)代理既可以代理接口,也可以代理類(lèi)。先寫(xiě)出代理類(lèi)的格式(假設(shè)代理的是類(lèi)TeacherChan):

public class $Proxy0 extends TeacherChan {

private InvocationHandler handler;

private static Method m0;

static {
    try {
        // 利用反射獲取TeacherChan的teach()方法
        m0 = TeacherChan.class.getMethod("teach", new Class[]{});
    } catch (NoSuchMethodException ne) {
        throw new NoSuchMethodError(ne.getMessage());
    }
}

// 構(gòu)造方法中傳入代理類(lèi)處理器
public $Proxy0(InvocationHandler handler) {
    this.handler = handler;
}

public void teach() {
    try {
        // 收集teach()方法傳入的參數(shù),此處參數(shù)為空
        Object[] args = new Object[]{};
        // 執(zhí)行代理類(lèi)的teach()
        Object result = handler.invoke(this, m0, args);
        // 如果有返回值,此處要返回result
    } catch (Error|RuntimeException e) {
        throw e;
    } catch (Throwable t) {
        throw new UndeclaredThrowableException(t);
    }
}

}

好了,生成類(lèi)的格式大概就是這樣設(shè)計(jì),實(shí)際編寫(xiě)代碼時(shí)需要處理參數(shù)、返回值和異常的情況,略微有點(diǎn)繁瑣。下面是動(dòng)態(tài)類(lèi)生成器:

public class MyProxyGenerator {

    // 換行符
    public static final String LINE_SEPARATOR = "
";
    // 動(dòng)態(tài)代理類(lèi)包名
    public static final String PROXY_CLASS_PACKAGE = "com.demos.proxy";
    // 動(dòng)態(tài)代理類(lèi)名前綴
    public static final String PROXY_CLASS_NAME_PREFIX = "$Proxy";
    // 動(dòng)態(tài)代理類(lèi)文件索引
    public static final AtomicLong INDEX_GENERATOR = new AtomicLong();
    // 動(dòng)態(tài)代理生成文件臨時(shí)目錄
    public static final String PROXY_CLASS_FILE_PATH = "/temp";

    /**
     * 生成代理類(lèi)并加載到JVM
     * @param clazz
     * @param methods
     * @throws Exception
     */
    public static Class generateAndLoadProxyClass(Class clazz, Method[] methods) throws Exception {
        long index = INDEX_GENERATOR.getAndIncrement();
        // 代理類(lèi)類(lèi)名
        String className = PROXY_CLASS_NAME_PREFIX + index;
        String fileName = PROXY_CLASS_FILE_PATH + File.separator + className + ".java";
        FileWriter writer = null;
        try {
            // 生成.java文件
            writer = new FileWriter(new File(fileName));
            writer.write(generateClassCode(PROXY_CLASS_PACKAGE, className, clazz, methods));
            writer.flush();
            // 編譯.java文件
            compileJavaFile(fileName);
            // 加載class到JVM
            String classPath = PROXY_CLASS_FILE_PATH + File.separator + className + ".class";
            Class proxyClass = MyClassLoader.getInstance().findClass(classPath, PROXY_CLASS_PACKAGE + "." + className);
            return proxyClass;
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

    /**
     * 編譯.java文件
     * @param fileName
     * @throws IOException
     */
    private static void compileJavaFile(String fileName) throws IOException {
        compileByTools(fileName);
//        compileByExec(fileName);
    }

    /**
     * 使用Runtime執(zhí)行javac命令
     * 注意: 需要指定classpath, 否則找不到依賴(lài)的類(lèi)
     * 建議使用compileByTools()
     * @param fileName
     * @throws IOException
     */
    @Deprecated
    private static void compileByExec(String fileName) throws IOException {
        // 獲取當(dāng)前的classpath
        String classpath = MyProxyGenerator.class.getResource("/").getPath();
        // 運(yùn)行命令: javac -classpath ${classpath} ${filepath}
        String command = "javac -classpath " + classpath + " " + fileName;
        Process process = Runtime.getRuntime().exec(command);
        // 等待執(zhí)行, 并輸出錯(cuò)誤日志
        try {
            InputStream errorStream = process.getErrorStream();
            InputStreamReader inputStreamReader = new InputStreamReader(errorStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println(line);
            }
            int exitVal = process.waitFor();
            System.out.println("Process exitValue: " + exitVal);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用JDK自帶的JavaCompiler
     * @param fileName
     * @throws IOException
     */
    private static void compileByTools(String fileName) throws IOException {
        // 獲取系統(tǒng)Java編譯器
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        // 獲取標(biāo)準(zhǔn)文件管理器實(shí)例
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        try {
            Iterable units = fileManager.getJavaFileObjects(fileName);
            JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units);
            task.call();
        } finally {
            fileManager.close();
        }
    }

    /**
     * 拼接 class 代碼片段
     * @param packageName   代理類(lèi)包名
     * @param clazz         要代理的類(lèi)型
     * @return
     */
    private static String generateClassCode(String packageName, String className, Class clazz, Method[] methods) throws Exception {

        StringBuilder classCodes = new StringBuilder();

        /*--------------------包名和依賴(lài) start--------------------*/
        classCodes.append("package ").append(packageName).append(";").append(LINE_SEPARATOR);
        classCodes.append(LINE_SEPARATOR);
        classCodes.append("import java.lang.reflect.*;").append(LINE_SEPARATOR);
        classCodes.append(LINE_SEPARATOR);
        /*--------------------包名和依賴(lài) start--------------------*/

        /*--------------------類(lèi)定義 start--------------------*/
        classCodes.append("public class ").append(className);
        if (clazz.isInterface()) {
            classCodes.append(" implements ");
        } else {
            classCodes.append(" extends ");
        }
        classCodes.append(clazz.getName()).append(" {").append(LINE_SEPARATOR);
        classCodes.append(LINE_SEPARATOR);
        /*--------------------類(lèi)定義 end--------------------*/

        /*--------------------聲明變量InvocationHandler start--------------------*/
        classCodes.append("private InvocationHandler handler;").append(LINE_SEPARATOR);
        classCodes.append(LINE_SEPARATOR);
        /*--------------------聲明變量InvocationHandler end--------------------*/

        /*--------------------聲明代理方法 start--------------------*/
        for (int i = 0; i < methods.length; i++) {
            classCodes.append("private static Method m").append(i).append(";").append(LINE_SEPARATOR);
        }
        classCodes.append(LINE_SEPARATOR);
        /*--------------------聲明代理方法 end--------------------*/

        /*--------------------代理方法對(duì)象初始化 start--------------------*/
        classCodes.append("static {").append(LINE_SEPARATOR);
        classCodes.append("    ").append("try {").append(LINE_SEPARATOR);
        for (int i = 0; i < methods.length; i++) {
            Method method = methods[i];
            classCodes.append("    ").append("    ").append("m").append(i).append(" = ").append(clazz.getName())
                    .append(".class.getMethod("").append(method.getName()).append("", new Class[]{");
            // 方法參數(shù)
            Parameter[] params = method.getParameters();
            if (params.length != 0) {
                for (int j = 0; j < params.length; j++) {
                    if (j != 0) {
                        classCodes.append(", ");
                    }
                    Parameter param = params[j];
                    classCodes.append(param.getType().getName()).append(".class");
                }
            }
            classCodes.append("});").append(LINE_SEPARATOR);
        }
        classCodes.append("    ").append("} catch (NoSuchMethodException ne) {").append(LINE_SEPARATOR);
        classCodes.append("    ").append("    ").append("throw new NoSuchMethodError(ne.getMessage());").append(LINE_SEPARATOR);
        classCodes.append("    ").append("}").append(LINE_SEPARATOR);
        classCodes.append("}").append(LINE_SEPARATOR);
        classCodes.append(LINE_SEPARATOR);
        /*--------------------代理方法對(duì)象初始化 end--------------------*/

        /*--------------------定義構(gòu)造函數(shù) start--------------------*/
        classCodes.append("public ").append(className).append("(InvocationHandler handler) {").append(LINE_SEPARATOR);
        classCodes.append("    ").append("this.handler = handler;").append(LINE_SEPARATOR);
        classCodes.append("}").append(LINE_SEPARATOR);
        classCodes.append(LINE_SEPARATOR);
        /*--------------------定義構(gòu)造函數(shù) end--------------------*/

        /*--------------------填充其他函數(shù) start--------------------*/
        classCodes.append(generateMethodCode(clazz, methods));
        /*--------------------填充其他函數(shù) end--------------------*/

        // 類(lèi)結(jié)束
        classCodes.append("}").append(LINE_SEPARATOR);

        return classCodes.toString();
    }

    /**
     * 拼接 method 代碼片段
     * @param clazz
     * @param methods
     * @return
     * @throws Exception
     */
    private static String generateMethodCode(Class clazz, Method[] methods) throws Exception {

        StringBuilder methodCodes = new StringBuilder();

        for (int i = 0; i < methods.length; i++) {
            Method method = methods[i];
            // 返回類(lèi)型
            String returnType = method.getReturnType().getName();
            // 參數(shù)列表
            Parameter[] params = method.getParameters();
            // 異常列表
            Class[] exceptionTypes = method.getExceptionTypes();

            /*--------------------方法定義 start--------------------*/
            methodCodes.append("public ").append(returnType).append(" ").append(method.getName());
            methodCodes.append("(");
            // 填充參數(shù)
            if (params.length != 0) {
                for (int j = 0; j < params.length; j++) {
                    if (j != 0) {
                        methodCodes.append(", ");
                    }
                    Parameter param = params[j];
                    methodCodes.append(param.getType().getName()).append(" ").append(param.getName());
                }
            }
            methodCodes.append(")");
            // 填充異常
            if (exceptionTypes.length != 0) {
                methodCodes.append(" throws ");
                for (int j = 0; j < exceptionTypes.length; j++) {
                    if (j != 0) {
                        methodCodes.append(", ");
                    }
                    methodCodes.append(exceptionTypes[j].getName());
                }
            }
            methodCodes.append(" {").append(LINE_SEPARATOR);
            /*--------------------方法定義 end--------------------*/

            /*--------------------方法體 start--------------------*/
            methodCodes.append("    ").append("try {").append(LINE_SEPARATOR);
            // 方法參數(shù)
            methodCodes.append("    ").append("    ").append("Object[] args = new Object[]{");
            if (params.length != 0) {
                for (int j = 0; j < params.length; j++) {
                    if (j != 0) {
                        methodCodes.append(", ");
                    }
                    Parameter param = params[j];
                    methodCodes.append(param.getName());
                }
            }
            methodCodes.append("};").append(LINE_SEPARATOR);
            // 執(zhí)行InvocationHandler.invoke()
            methodCodes.append("    ").append("    ").append("Object result = handler.invoke(this, m").append(i)
                    .append(", args);").append(LINE_SEPARATOR);
            // 返回結(jié)果
            if (!"void".equals(returnType)) {
                methodCodes.append("    ").append("    ").append("return (").append(returnType).append(") result;").append(LINE_SEPARATOR);
            }
            // 異常處理
            methodCodes.append("    ").append("} catch (Error|RuntimeException");
            for (Class exceptionType : exceptionTypes) {
                methodCodes.append("|").append(exceptionType.getName());
            }
            methodCodes.append(" e) {").append(LINE_SEPARATOR);
            methodCodes.append("    ").append("    ").append("throw e;").append(LINE_SEPARATOR);
            methodCodes.append("    ").append("} catch (Throwable t) {").append(LINE_SEPARATOR);
            methodCodes.append("    ").append("    ").append("throw new UndeclaredThrowableException(t);").append(LINE_SEPARATOR);
            methodCodes.append("    ").append("}").append(LINE_SEPARATOR);
            /*--------------------方法體 end--------------------*/

            // 方法結(jié)束
            methodCodes.append("}").append(LINE_SEPARATOR).append(LINE_SEPARATOR);
        }

        return methodCodes.toString();
    }

}

實(shí)際上只是拼接前面給出的代理類(lèi)實(shí)現(xiàn)而已,代碼量有點(diǎn)大,但并不難理解。

下一步,實(shí)現(xiàn)自己的類(lèi)加載器,來(lái)加載生成的class字節(jié)碼:

public class MyClassLoader extends ClassLoader {

    private static MyClassLoader loader;

    private MyClassLoader() {

    }

    public static MyClassLoader getInstance() {
        if (loader == null) {
            synchronized (MyClassLoader.class) {
                // 得到鎖首先檢查loader是否已經(jīng)存在, 避免重復(fù)創(chuàng)建
                if (loader == null) {
                    loader = new MyClassLoader();
                }
            }
        }
        return loader;
    }

    /**
     * 加載class文件,并返回類(lèi)型對(duì)象
     *
     * @param filePath
     * @param className
     * @return
     * @throws ClassNotFoundException
     */
    public Class findClass(String filePath, String className) throws ClassNotFoundException {
        try {
            // 讀取指定class文件的字節(jié)碼
            byte[] classBytes = Files.readAllBytes(Paths.get(filePath));
            // 加載類(lèi)并返回class類(lèi)型對(duì)象
            Class clazz = defineClass(className, classBytes, 0, classBytes.length);
            return clazz;
        } catch (IOException e) {
            e.printStackTrace();
        }
        throw new ClassNotFoundException(className);
    }

}

到這一步,復(fù)雜的工作基本做完了,接下來(lái)只剩下自定義處理器類(lèi)和對(duì)外接口了

自定義處理器類(lèi)與Java動(dòng)態(tài)代理的方式相同:

public class MyInvocationHandler implements InvocationHandler {

    private Object proxied;

    public MyInvocationHandler(Object object) {
        this.proxied = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("-------------------老師好[by 自定義動(dòng)態(tài)代理]-------------------");
        Object obj = method.invoke(proxied, args);
        System.out.println("-------------------老師再見(jiàn)[by 自定義動(dòng)態(tài)代理]-------------------");
        return obj;
    }

}

對(duì)外接口:

public class MyDynamicProxy {

    public static  T newProxyInstance(Class clazz, InvocationHandler handler) throws Exception {
        // 要代理的方法: public & !final
        Method[] proxyMethods = Arrays.stream(clazz.getMethods())
                .filter(method -> !Modifier.isFinal(method.getModifiers()))
                .collect(Collectors.toList())
                .toArray(new Method[0]);
        // 生成的代理類(lèi)
        Class proxyClass = MyProxyGenerator.generateAndLoadProxyClass(clazz, proxyMethods);
        // 代理類(lèi)的構(gòu)造方法
        Constructor c = proxyClass.getConstructor(InvocationHandler.class);
        // 創(chuàng)建代理類(lèi)對(duì)象
        Object proxyObj = c.newInstance(handler);
        return (T) proxyObj;
    }

}

搞定,測(cè)試一下效果:

TeacherChan proxy1 = MyDynamicProxy.newProxyInstance(
        TeacherChan.class, new MyInvocationHandler(new TeacherChan()));
proxy1.teach();

TeacherCang proxy2 = MyDynamicProxy.newProxyInstance(
        TeacherCang.class, new MyInvocationHandler(new TeacherCang()));
proxy2.teach();

輸出:

-------------------老師好[by 自定義動(dòng)態(tài)代理]-------------------
大家好,我是陳老師,我教大家攝影!
-------------------老師再見(jiàn)[by 自定義動(dòng)態(tài)代理]-------------------
-------------------老師好[by 自定義動(dòng)態(tài)代理]-------------------
大家好,我是蒼老師,我教大家生物!
-------------------老師再見(jiàn)[by 自定義動(dòng)態(tài)代理]-------------------

完美!大功告成!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/70841.html

相關(guān)文章

  • spring-cloud-feign源碼深度解析

    摘要:內(nèi)部使用了的動(dòng)態(tài)代理為目標(biāo)接口生成了一個(gè)動(dòng)態(tài)代理類(lèi),這里會(huì)生成一個(gè)動(dòng)態(tài)代理原理統(tǒng)一的方法攔截器,同時(shí)為接口的每個(gè)方法生成一個(gè)攔截器,并解析方法上的元數(shù)據(jù),生成一個(gè)請(qǐng)求模板。的核心源碼解析到此結(jié)束了,不知道是否對(duì)您有無(wú)幫助,可留言跟我交流。 Feign是一個(gè)聲明式的Web服務(wù)客戶(hù)端。這使得Web服務(wù)客戶(hù)端的寫(xiě)入更加方便 要使用Feign創(chuàng)建一個(gè)界面并對(duì)其進(jìn)行注釋。它具有可插拔注釋支持,包...

    vibiu 評(píng)論0 收藏0
  • 手把手教你基于Netty實(shí)現(xiàn)一個(gè)基礎(chǔ)的RPC框架(通俗易懂)

    摘要:是一個(gè)分布式服務(wù)框架,以及治理方案。手寫(xiě)注意要點(diǎn)手寫(xiě)注意要點(diǎn)基于上文中對(duì)于協(xié)議的理解,如果我們自己去實(shí)現(xiàn),需要考慮哪些技術(shù)呢其實(shí)基于圖的整個(gè)流程應(yīng)該有一個(gè)大概的理解?;谑謱?xiě)實(shí)現(xiàn)基于手寫(xiě)實(shí)現(xiàn)理解了協(xié)議后,我們基于來(lái)實(shí)現(xiàn)一個(gè)通信框架。閱讀這篇文章之前,建議先閱讀和這篇文章關(guān)聯(lián)的內(nèi)容。[1]詳細(xì)剖析分布式微服務(wù)架構(gòu)下網(wǎng)絡(luò)通信的底層實(shí)現(xiàn)原理(圖解)[2][年薪60W的技巧]工作了5年,你真的理解N...

    番茄西紅柿 評(píng)論0 收藏2637
  • Java面試 32個(gè)核心必考點(diǎn)完全解析

    摘要:如問(wèn)到是否使用某框架,實(shí)際是是問(wèn)該框架的使用場(chǎng)景,有什么特點(diǎn),和同類(lèi)可框架對(duì)比一系列的問(wèn)題。這兩個(gè)方向的區(qū)分點(diǎn)在于工作方向的側(cè)重點(diǎn)不同。 [TOC] 這是一份來(lái)自嗶哩嗶哩的Java面試Java面試 32個(gè)核心必考點(diǎn)完全解析(完) 課程預(yù)習(xí) 1.1 課程內(nèi)容分為三個(gè)模塊 基礎(chǔ)模塊: 技術(shù)崗位與面試 計(jì)算機(jī)基礎(chǔ) JVM原理 多線(xiàn)程 設(shè)計(jì)模式 數(shù)據(jù)結(jié)構(gòu)與算法 應(yīng)用模塊: 常用工具集 ...

    JiaXinYi 評(píng)論0 收藏0
  • cockroach 爬蟲(chóng):又一個(gè) java 爬蟲(chóng)實(shí)現(xiàn)

    摘要:爬蟲(chóng)又一個(gè)爬蟲(chóng)實(shí)現(xiàn)原文簡(jiǎn)介小強(qiáng)當(dāng)時(shí)不知道為啥選了這么個(gè)名字,又長(zhǎng)又難記,導(dǎo)致編碼的過(guò)程中因?yàn)閱卧~的拼寫(xiě)問(wèn)題耽誤了好長(zhǎng)時(shí)間。我是一個(gè)小強(qiáng)爬蟲(chóng)線(xiàn)程數(shù)健壯說(shuō)到健壯,這里主要體現(xiàn)在以下幾個(gè)方面應(yīng)對(duì)封鎖這里我們使用動(dòng)態(tài)代理來(lái)解決這個(gè)問(wèn)題。 cockroach 爬蟲(chóng):又一個(gè) java 爬蟲(chóng)實(shí)現(xiàn) 原文 簡(jiǎn)介 cockroach[小強(qiáng)] 當(dāng)時(shí)不知道為啥選了這么個(gè)名字,又長(zhǎng)又難記,導(dǎo)致編碼的過(guò)程中因?yàn)閱?..

    liangzai_cool 評(píng)論0 收藏0
  • Spring自定義注解不生效原因解析及解決方法

    摘要:自定義注解不生效原因解析及解決方法背景項(xiàng)目中,自己基于實(shí)現(xiàn)了一套緩存注解。但是最近出現(xiàn)一種情況緩存竟然沒(méi)有生效,大量請(qǐng)求被擊穿到層,導(dǎo)致壓力過(guò)大。至此,問(wèn)題得到解決。 自定義注解不生效原因解析及解決方法 背景: 項(xiàng)目中,自己基于spring AOP實(shí)現(xiàn)了一套java緩存注解。但是最近出現(xiàn)一種情況:緩存竟然沒(méi)有生效,大量請(qǐng)求被擊穿到db層,導(dǎo)致db壓力過(guò)大?,F(xiàn)在我們看一下具體代碼情形(代...

    xbynet 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<