1. 程式人生 > >設計模式(23)-----代理設計模式-----動態代理

設計模式(23)-----代理設計模式-----動態代理

 一:自己手寫動態代理

1.1,把一串靜態的字串(代理類)載入輸出,編譯,載入到記憶體,並且反射獲取這個物件

public interface Moveable {
    public void move();
}

 

 1 package com.DesignPatterns.al.Dynamic5;
 2 
 3 import java.util.Random;
 4 
 5 public class Tank implements Moveable {
 6 
 7     @Override
 8     public void move() {
9 10 System.out.println("坦克在移動"); 11 try { 12 Thread.sleep(new Random().nextInt(1000)); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 } 16 17 } 18 }

 

 

package com.DesignPatterns.al.Dynamic5;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class Test { public static void main(String[] args) throws Exception { String rt="\r\n"; String src= "package com.DesignPatterns.al.Dynamic5;"+rt+ "public class TankTime implements Moveable {"+rt+ " Moveable t;"+rt+ "
public TankTime(Moveable t) {"+rt+ " super();"+rt+ " this.t = t;"+rt+ " }"+rt+ " @Override"+rt+ " public void move() {"+rt+ " long startTime = System.currentTimeMillis();"+rt+ " System.out.println(\"tank開始列印開始時間\"+startTime);"+rt+ " t.move();"+rt+ " long endTime = System.currentTimeMillis();"+rt+ " System.out.println(\"tank總共花費時間是\" + (endTime - startTime));"+rt+ " }"+rt+ "}"; //1,生成代理類 String fileName = System.getProperty("user.dir"); System.out.println("fileName是專案的根路徑:"+fileName); String fileNameEnd=fileName+"/src/com/DesignPatterns/al/Dynamic5/TankTime.java"; File f=new File(fileNameEnd); FileWriter fw=new FileWriter(f); fw.write(src); fw.flush(); fw.close(); //2,開始生成class檔案,進行編譯 //2.1,獲取java預設的編譯器,說白了就是javac JavaCompiler compiler=ToolProvider.getSystemJavaCompiler(); System.out.println("現在我的編譯器是"+compiler.getClass().getName()); StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null); Iterable units = fileMgr.getJavaFileObjects(fileNameEnd); CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units); //2.2,生成class檔案這裡不是重點,我們知道通過上面的程式碼就可以把java程式碼生成到指定的目錄下面的class檔案 t.call(); fileMgr.close(); //3,把class檔案載入到記憶體中去 URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir") +"/src")}; URLClassLoader ul = new URLClassLoader(urls); Class c = ul.loadClass("com.DesignPatterns.al.Dynamic5.TankTime"); System.out.println("載入我的class檔案是"+c); //4,從記憶體中生成物件,因為這個時候我們沒有這個java類,所以只有從記憶體中來區這個值。 //換句話就是說,我們現在沒有這個物件,只有通過反射(通過構造器反射來獲取這個物件) //4.1,我們呼叫的是構造方法的引數為Moveable(上面的引數是它的實現也可以的)型別的構造方法 Constructor ctr = c.getConstructor(Moveable.class); Moveable m = (Moveable)ctr.newInstance(new Tank()); m.move(); } }

 

 

通過執行,我們得到動態獲取下面的程式碼:

package com.DesignPatterns.al.Dynamic5;
public class TankTime implements Moveable {
    Moveable t;
    public TankTime(Moveable t) {
        super();
        this.t = t;
    }
    @Override
    public void move() {
        long startTime = System.currentTimeMillis();
        System.out.println("tank開始列印開始時間"+startTime);
        t.move();
        long endTime = System.currentTimeMillis();
        System.out.println("tank總共花費時間是" + (endTime - startTime));
    }
}

 

控制檯打印出下面的

fileName是專案的根路徑:D:\data\eclipse-workspace\DesignPatterns
現在我的編譯器是com.sun.tools.javac.api.JavacTool
載入我的class檔案是class com.DesignPatterns.al.Dynamic5.TankTime
tank開始列印開始時間1541264914598
坦克在移動
tank總共花費時間是531

 

1.2,升級------ 靜態的字串(代理類)------裡面的介面變成動態的

package com.DesignPatterns.al.Dynamic6;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class Proxy {
public static Object newProxyInstance(Class inferance) throws Exception {
    String rt="\r\n";
    String src=
            "package com.DesignPatterns.al.Dynamic6;"+rt+

            "public class TankTime implements "+inferance.getName()+ "{"+rt+
            "    Moveable t;"+rt+

            "    public TankTime(Moveable t) {"+rt+
            "        super();"+rt+
            "        this.t = t;"+rt+
            "    }"+rt+

            "    @Override"+rt+
            "    public void move() {"+rt+

            "        long startTime = System.currentTimeMillis();"+rt+
            "        System.out.println(\"tank開始列印開始時間\"+startTime);"+rt+
            "        t.move();"+rt+
            "        long endTime = System.currentTimeMillis();"+rt+
            "        System.out.println(\"tank總共花費時間是\" + (endTime - startTime));"+rt+
            "    }"+rt+

            "}";
    //1,生成代理類
    String fileName = System.getProperty("user.dir");
    System.out.println("fileName是專案的根路徑:"+fileName);
    String fileNameEnd=fileName+"/src/com/DesignPatterns/al/Dynamic6/TankTime.java";
    
    File f=new File(fileNameEnd);
    FileWriter fw=new FileWriter(f);
    fw.write(src);
    fw.flush();
    fw.close();
    
    //2,開始生成class檔案,進行編譯
    //2.1,獲取java預設的編譯器,說白了就是javac
    JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
    System.out.println("現在我的編譯器是"+compiler.getClass().getName());
    StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
    Iterable units = fileMgr.getJavaFileObjects(fileNameEnd);
    CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
    //2.2,生成class檔案這裡不是重點,我們知道通過上面的程式碼就可以把java程式碼生成到指定的目錄下面的class檔案
    t.call();
    fileMgr.close();
    
    
    //3,把class檔案載入到記憶體中去
    URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir") +"/src")};
    URLClassLoader ul = new URLClassLoader(urls);
    Class c = ul.loadClass("com.DesignPatterns.al.Dynamic6.TankTime");
    System.out.println("載入我的class檔案是"+c);
    
    //4,從記憶體中生成物件
    //4.1,我們呼叫的是構造方法的引數為Moveable(上面的引數是它的實現也可以的)型別的構造方法
    Constructor ctr = c.getConstructor(Moveable.class);
    Moveable m = (Moveable)ctr.newInstance(new Tank());
    m.move();
    return null;
    
}
}

 

1.3,升級------ 靜態的字串(代理類)------裡面的介面變成動態的----裡面的方法變成動態的

 

 

public interface Moveable {
    public void move();
}

 

 

public class Test {
    public static void main(String[] args) {
        Method[] methods = Moveable.class.getMethods();
        for(Method m : methods) {
            System.out.println(m.getName());
        }
    }
}

 

上面的程式碼是通過發射動態的來獲取方法的例項。看下面真正的改造

package com.DesignPatterns.al.Dynamic8;

public interface Moveable {
    public void move();
}
package com.DesignPatterns.al.Dynamic8;

import java.util.Random;

public class Tank implements Moveable {

    @Override
    public void move() {
        
        System.out.println("坦克在移動");
        try {
            Thread.sleep(new Random().nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
    }
}
package com.DesignPatterns.al.Dynamic8;

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;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class Proxy {
public static Object newProxyInstance(Class inferance) throws Exception {
    String methodStr = "";
    Method[] methods = inferance.getMethods();
    String rt="\r\n";
    
    for(Method m : methods) {
        methodStr += "@Override" + rt + 
                     "public void " + m.getName() + "() {" + rt +
                         "   long start = System.currentTimeMillis();" + rt +
                        "   System.out.println(\"starttime:\" + start);" + rt +
                        "   t." + m.getName() + "();" + rt +
                        "   long end = System.currentTimeMillis();" + rt +
                        "   System.out.println(\"time:\" + (end-start));" + rt +
                     "}";
    }
    String src=
            "package com.DesignPatterns.al.Dynamic8;"+rt+

            "public class TankTime implements "+inferance.getName()+ "{"+rt+
            "    Moveable t;"+rt+

            "    public TankTime(Moveable t) {"+rt+
            "        super();"+rt+
            "        this.t = t;"+rt+
            "    }"+rt+

            methodStr +rt+

            "}";
    //1,生成代理類
    String fileName = System.getProperty("user.dir");
    System.out.println("fileName是專案的根路徑:"+fileName);
    String fileNameEnd=fileName+"/src/com/DesignPatterns/al/Dynamic8/TankTime.java";
    
    File f=new File(fileNameEnd);
    FileWriter fw=new FileWriter(f);
    fw.write(src);
    fw.flush();
    fw.close();
    
    //2,開始生成class檔案,進行編譯
    //2.1,獲取java預設的編譯器,說白了就是javac
    JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
    System.out.println("現在我的編譯器是"+compiler.getClass().getName());
    StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
    Iterable units = fileMgr.getJavaFileObjects(fileNameEnd);
    CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
    //2.2,生成class檔案這裡不是重點,我們知道通過上面的程式碼就可以把java程式碼生成到指定的目錄下面的class檔案
    t.call();
    fileMgr.close();
    
    
    //3,把class檔案載入到記憶體中去
    URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir") +"/src")};
    URLClassLoader ul = new URLClassLoader(urls);
    Class c = ul.loadClass("com.DesignPatterns.al.Dynamic8.TankTime");
    System.out.println("載入我的class檔案是"+c);
    
    //4,從記憶體中生成物件
    //4.1,我們呼叫的是構造方法的引數為Moveable(上面的引數是它的實現也可以的)型別的構造方法
    Constructor ctr = c.getConstructor(Moveable.class);
    Object o = ctr.newInstance(new Tank());
    return o;
    
}
}

 

package com.DesignPatterns.al.Dynamic8;

/**
 * 
 * @author qingruihappy
 * @data 2018年11月1日 上午12:39:35
 * @說明:現在模擬動態的方法並且列印主方法的內容
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Moveable newProxyInstance = (Moveable) Proxy.newProxyInstance(Moveable.class);
        // 是不是很神奇啊,現在假如我們現在沒有看到代理類,我們只有介面類,tank類,還有測試類,
        // 我們直觀的來看現在的方法就是,我們把介面傳入進入了newProxy這個方法中,它居然動態的給我
        // 打印出了日誌還有主方法。
        // 我們不看代理類裡面的程式碼我們閉著眼來想一下它是怎麼實現的。
        // 1,首先把不用變的程式碼以靜態string的形式寫入進了動態類中
        // 2,把動態變化的東西比如說介面,通過傳入的介面來獲取,
        // 3,方法,因為介面中就有了實現類的方法,所以通過介面也能獲取,其中的方法,
        // 4,那它是怎麼來確定實現類就是tank呢而不是其他的呢。往下面看
        // 5,寫完了之後生成java檔案
        // 6,編譯生成class檔案
        // 7,載入到記憶體中來
        // 8,從內從中反射生成物件。(注意這這裡傳入了tank確定了是tank物件,所以當下面呼叫)
        // 9,執行下面的方法,就相當於執行剛生成的tanktime裡面的方法是一樣的。
        newProxyInstance.move();
        // 我們來看看還有什麼問題就是動態切入進來的程式碼不是隨機變化的,下面就是讓切入進來的程式碼變成隨機變化的。

    }

}

 

上面的程式碼執行之後動態生成的java代理類是:

package com.DesignPatterns.al.Dynamic8;
public class TankTime implements com.DesignPatterns.al.Dynamic8.Moveable{
    Moveable t;
    public TankTime(Moveable t) {
        super();
        this.t = t;
    }
@Override
public void move() {
   long start = System.currentTimeMillis();
   System.out.println("starttime:" + start);
   t.move();
   long end = System.currentTimeMillis();
   System.out.println("time:" + (end-start));
}
}

 

控制檯打印出來的是:

fileName是專案的根路徑:D:\data\eclipse-workspace\DesignPatterns
現在我的編譯器是com.sun.tools.javac.api.JavacTool
載入我的class檔案是class com.DesignPatterns.al.Dynamic8.TankTime
starttime:1541265534048
坦克在移動
time:877

 

 

 

 

1.4,升級------ 靜態的字串(代理類)------裡面的介面變成動態的----裡面的方法變成動態的-----非業務程式碼的抽取(關鍵)

package com.DesignPatterns.al.Dynamic9;

import java.lang.reflect.Method;

public interface InvocationHandler {
    public void invoke(Object o, Method m);
}

 

 

package com.DesignPatterns.al.Dynamic9;

import java.lang.reflect.Method;

public class TimeHandler implements InvocationHandler{
    //這個target在test類中傳參的時候就已經傳入進來了
    private Object target;



    public TimeHandler(Object target) {
        super();
        this.target = target;
    }



    @Override
    public void invoke(Object o, Method m) {
        //Object o這裡的o是代理物件的例項,這裡我們沒用,並不代表以後我們不用
        System.out.println("代理類是誰呢?"+o.getClass().getName());
        long start = System.currentTimeMillis();
        System.out.println("starttime:" + start);
        try {
            //target是目標類不是代理類。目標類的方法,而不是代理類的方法。這裡invoke是反射特有的方法,就是類(target).方法(m)
            m.invoke(target);
        } catch (Exception e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("time:" + (end-start));
    }

}
package com.DesignPatterns.al.Dynamic9;

public interface Moveable {
    public void move();
}
package com.DesignPatterns.al.Dynamic9;

import java.util.Random;

public class Tank implements Moveable {

    @Override
    public void move() {
        
        System.out.println("坦克在移動");
        try {
            Thread.sleep(new Random().nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
    }
}
package com.DesignPatterns.al.Dynamic9;

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;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

/**
 * jdk中的類名是$Proxy1,可以把下面的TankTime替換掉,其實就是一個名字意義不大,都一樣
 * 
 * @author qingruihappy
 * @data 2018年11月2日 上午1:24:30
 * @說明:其實可以這樣理解對inferance接口裡面的方法實行的是什麼樣(InvocationHandler)日誌,時間,事物。。。等等的代理
 */
public class Proxy {
    public static Object newProxyInstance(Class inferance, InvocationHandler h) throws Exception {
        String methodStr = "";
        Method[] methods = inferance.getMethods();
        String rt = "\r\n";

        for (Method m : methods) {
            methodStr += "@Override" + rt + "public void " + m.getName() + "() {" + rt + "    try {" + rt
                    + "    Method md = " + inferance.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt
                    + "    h.invoke(this, md);" + rt + "    }catch(Exception e) {e.printStackTrace();}" + rt +

                    "}";
        }
        String src = "package com.DesignPatterns.al.Dynamic9;" + rt + "import java.lang.reflect.Method;" + rt
                + "public class TankTime implements " + inferance.getName() + "{" + rt +

                "    public TankTime(InvocationHandler h) {" + rt + "        super();" + rt + "        this.h = h;" + rt
                + "    }" + rt + "    com.DesignPatterns.al.Dynamic9.InvocationHandler h;" + rt + methodStr + rt +

                "}";
        // 1,生成代理類
        String fileName = System.getProperty("user.dir");
        System.out.println("fileName是專案的根路徑:" + fileName);
        String fileNameEnd = fileName + "/src/com/DesignPatterns/al/Dynamic9/TankTime.java";

        File f = new File(fileNameEnd);
        FileWriter fw = new FileWriter(f);
        fw.write(src);
        fw.flush();
        fw.close();

        // 2,開始生成class檔案,進行編譯
        // 2.1,獲取java預設的編譯器,說白了就是javac
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        System.out.println("現在我的編譯器是" + compiler.getClass().getName());
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        Iterable units = fileMgr.getJavaFileObjects(fileNameEnd);
        CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
        // 2.2,生成class檔案這裡不是重點,我們知道通過上面的程式碼就可以把java程式碼生成到指定的目錄下面的class檔案
        t.call();
        fileMgr.close();

        // 3,把class檔案載入到記憶體中去
        URL[] urls = new URL[] { new URL("file:/" + System.getProperty("user.dir") + "/src") };
        URLClassLoader ul = new URLClassLoader(urls);
        Class c = ul.loadClass("com.DesignPatterns.al.Dynamic9.TankTime");
        System.out.println("載入我的class檔案是" + c);

        // 4,從記憶體中生成物件
        // 4.1,我們呼叫的是構造方法的引數為Moveable(上面的引數是它的實現也可以的)型別的構造方法
        // 其實在這裡我們可以更好的理解反射,傳統意義上的程式碼我們都是現有java實體類,讓後我們在去new的方法創造記憶體物件
        // 但是假如現在我們有了使我們動態的java檔案,並且已經生成class檔案載入到記憶體中了
        // 但是由於java檔案不是我們自己寫的,所以我們現在我們看不到java檔案,所以就沒有辦法new物件了
        // 但是我們在記憶體中已經有了這個物件了,現在我們要用這個物件,這個時候就需要用發射了

        // 下面的程式碼就是實現了InvocationHandler介面的構造器反射獲取構造器,然後再通過newInstance獲取物件,h是介面具體的實現類
        Constructor ctr = c.getConstructor(InvocationHandler.class);
        Object o = ctr.newInstance(h);
        return o;

    }
}

 

package com.DesignPatterns.al.Dynamic9;

/**
 * 
 * @author qingruihappy
 * @data 2018年11月1日 上午12:39:35
 * @說明:現在模擬配置類隨意切換的問題
 * 
 * 現在可以對任意的物件,任意的介面方法,實現任意的代理
 *現在我們寫的差不多了,我們來捋一下思路
 *1,我們首先要知道動態代理要幹啥呢?
 *動態代理就是類似於面向切面程式設計,我們在不知不覺中間就把類似於日誌,事物,許可權,加解密等非業務程式碼邏輯加到了目標函式的前後。
 *2,我們來看一下,我們平常遇到的jdk動態代理是怎麼用的。
 *2.1,寫出目標(業務)函式,並且必須讓它實現一個介面,
 *2.2,寫出非業務程式碼的介面,並且讓子類來實現非業務程式碼
 *2.3,在非業務程式碼中又通過發射來呼叫業務程式碼,(注意這裡面無非就是目標類.目標方法,反射特有的invoke方法)
 *2.4,通過(Proxy)動態的獲取變化的代理類
 *3,動態的獲取變化的代理才是動態代理的難點,因為,生成代理類的proxy是封裝過得,而生成的代理的java程式碼我們也是看不到的
 *這個也就是難點
 *3.1,至於如何獲取請看下面
 *        // 1,首先把不用變的程式碼以靜態string的形式寫入進了動態類中
        // 2,介面:把動態變化的東西比如說介面,通過傳入的介面來獲取,
        // 3,方法:因為介面中就有了實現類的方法,所以通過介面也能獲取,其中的方法,
        // 4,回撥:把非業務程式碼的實現了傳入到Proxy中來,當呼叫第三步已經確定了的方法額時候,就明確的呼叫在非業務程式碼中的invoke方法
        //4.1,目標:在非業務程式碼的中間反射呼叫業務程式碼
        // 5,寫出:寫完了之後生成java檔案
        // 6,編譯:編譯生成class檔案
        // 7,記憶體:載入到記憶體中來
        // 8,反射:從內從中反射生成物件。(注意這這裡傳入了tank確定了是tank物件,所以當下面呼叫)
        //9,物件:獲取代理物件
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Tank t = new Tank();
        InvocationHandler h = new TimeHandler(t);
        Moveable m = (Moveable) Proxy.newProxyInstance(Moveable.class, h);
        m.move();
    }

}

 

 

生成的代理類是:

package com.DesignPatterns.al.Dynamic9;
import java.lang.reflect.Method;
public class TankTime implements com.DesignPatterns.al.Dynamic9.Moveable{
    public TankTime(InvocationHandler h) {
        super();
        this.h = h;
    }
    com.DesignPatterns.al.Dynamic9.InvocationHandler h;
@Override
public void move() {
    try {
    Method md = com.DesignPatterns.al.Dynamic9.Moveable.class.getMethod("move");
    h.invoke(this, md);
    }catch(Exception e) {e.printStackTrace();}
}
}

 

控制檯

fileName是專案的根路徑:D:\data\eclipse-workspace\DesignPatterns
現在我的編譯器是com.sun.tools.javac.api.JavacTool
載入我的class檔案是class com.DesignPatterns.al.Dynamic9.TankTime
代理類是誰呢?com.DesignPatterns.al.Dynamic9.TankTime
starttime:1541265881417
坦克在移動
time:698

 

 

 

1.5,升級------ 靜態的字串(代理類)------裡面的介面變成動態的----裡面的方法變成動態的-----非業務程式碼的抽取----用我們自己寫的動態代理測試在程式碼前後加事物

package com.DesignPatterns.al.Dynamic91;

import java.lang.reflect.Method;

public interface InvocationHandler {
    public void invoke(Object o, Method m);
}
package com.DesignPatterns.al.Dynamic91;

import java.lang.reflect.Method;

public class TransactionHandler implements InvocationHandler {
    
    private Object target;
    
    public TransactionHandler(Object target) {
        super();
        this.target = target;
    }

    @Override
    public void invoke(Object o, Method m) {
        System.out.println("Transaction Start");
        try {
            m.invoke(target);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Transaction Commit");
    }

}
package com.DesignPatterns.al.Dynamic91;

public interface UserMgr {
    void addUser();
}
package com.DesignPatterns.al.Dynamic91;

public class UserMgrImpl implements UserMgr {

    @Override
    public void addUser() {
        System.out.println("1: 插入記錄到user表");
        System.out.println("2: 做日誌在另外一張表");
    }
    
}
package com.DesignPatterns.al.Dynamic91;

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;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
/**
 * jdk中的類名是$Proxy1,可以把下面的TankTime替換掉,其實就是一個名字意義不大,都一樣
 * @author qingruihappy
 * @data   2018年11月2日 上午1:24:30
 * @說明:
 */
public class Proxy {
public static Object newProxyInstance(Class inferance,InvocationHandler h) throws Exception {
    String methodStr = "";
    Method[] methods = inferance.getMethods();
    String rt="\r\n";
    
    for(Method m : methods) {
        methodStr += "@Override" + rt + 
                     "public void " + m.getName() + "() {" + rt +
                     "    try {" + rt +
                     "    Method md = " + inferance.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
                     "    h.invoke(this, md);" + rt +
                     "    }catch(Exception e) {e.printStackTrace();}" + rt +
                    
                     "}";
    }
    String src=
            "package com.DesignPatterns.al.Dynamic91;"+rt+
            "import java.lang.reflect.Method;" + rt +
            "public class TankTime implements "+inferance.getName()+ "{"+rt+

            "    public TankTime(InvocationHandler h) {"+rt+
            "        super();"+rt+
            "        this.h = h;"+rt+
            "    }"+rt+
            "    com.DesignPatterns.al.Dynamic91.InvocationHandler h;" + rt +
            methodStr +rt+

            "}";
    //1,生成代理類
    String fileName = System.getProperty("user.dir");
    System.out.println("fileName是專案的根路徑:"+fileName);
    String fileNameEnd=fileName+"/src/com/DesignPatterns/al/Dynamic91/TankTime.java";
    
    File f=new File(fileNameEnd);
    FileWriter fw=new FileWriter(f);
    fw.write(src);
    fw.flush();
    fw.close();
    
    //2,開始生成class檔案,進行編譯
    //2.1,獲取java預設的編譯器,說白了就是javac
    JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
    System.out.println("現在我的編譯器是"+compiler.getClass().getName());
    StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
    Iterable units = fileMgr.getJavaFileObjects(fileNameEnd);
    CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
    //2.2,生成class檔案這裡不是重點,我們知道通過上面的程式碼就可以把java程式碼生成到指定的目錄下面的class檔案
    t.call();
    fileMgr.close();
    
    
    //3,把class檔案載入到記憶體中去
    URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir") +"/src")};
    URLClassLoader ul = new URLClassLoader(urls);
    Class c = ul.loadClass("com.DesignPatterns.al.Dynamic91.TankTime");
    System.out.println("載入我的class檔案是"+c);
    
    //4,從記憶體中生成物件
    //4.1,我們呼叫的是構造方法的引數為Moveable(上面的引數是它的實現也可以的)型別的構造方法
    Constructor ctr = c.getConstructor(InvocationHandler.class);
    Object o = ctr.newInstance(h);
    return o;
    
}
}
package com.DesignPatterns.al.Dynamic91;

public class Client {
    public static void main(String[] args) throws Exception {
        UserMgr mgr = new UserMgrImpl();
        InvocationHandler h = new TransactionHandler(mgr);
        UserMgr u = (UserMgr)Proxy.newProxyInstance(UserMgr.class,h);
        u.addUser();
    }
}

 

動態生成的代理類是:

package com.DesignPatterns.al.Dynamic91;
import java.lang.reflect.Method;
public class TankTime implements com.DesignPatterns.al.Dynamic91.UserMgr{
    public TankTime(InvocationHandler h) {
        super();
        this.h = h;
    }
    com.DesignPatterns.al.Dynamic91.InvocationHandler h;
@Override
public void addUser() {
    try {
    Method md = com.DesignPatterns.al.Dynamic91.UserMgr.class.getMethod("addUser");
    h.invoke(this, md);
    }catch(Exception e) {e.printStackTrace();}
}
}

 

控制檯:

fileName是專案的根路徑:D:\data\eclipse-workspace\DesignPatterns
現在我的編譯器是com.sun.tools.javac.api.JavacTool
載入我的class檔案是class com.DesignPatterns.al.Dynamic91.TankTime
Transaction Start
1: 插入記錄到user表
2: 做日誌在另外一張表
Transaction Commit

 

 二,我們來看jdk自帶的動態代理

案例一:

package com.DesignPatterns.al.Dynamic92;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @Author: feiweiwei
 * @Description:
 * @Created Date: 09:50 17/9/22.
 * @Modify by:
 */
public class DynamicProxyAOP implements InvocationHandler {
    private Object subject;

    public DynamicProxyAOP(Object subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在執行真實subject執行的方法
        System.out.println("before do something");
        //執行真實subject方法    //方法,物件,引數都有了就可以執行了。
        Object rtn = method.invoke(subject, args);
        //在執行結束後再執行的方法
        System.out.println("after do something");

        return rtn;
    }
}

 

package com.DesignPatterns.al.Dynamic92;

/**
 * @Author: feiweiwei
 * @Description:
 * @Created Date: 09:47 17/9/22.
 * @Modify by:
 */
public interface Subject {
    public String doSomething(String name);
}
package com.DesignPatterns.al.Dynamic92;

/**
 * @Author: feiweiwei
 * @Description:
 * @Created Date: 09:49 17/9/22.
 * @Modify by:
 */
public class RealSubject implements Subject {
    @Override
    public String doSomething(String name) {
        System.out.println(name + " do something!");
        return name + " do something!";
    }
}
package com.DesignPatterns.al.Dynamic92;

/**
 * @Author: feiweiwei
 * @Description:
 * @Created Date: 09:56 17/9/22.
 * @Modify by:
 */
public class RealSubject2 implements Subject {
    @Override
    public String doSomething(String name) {
        System.out.println(name + " do something2!");
        return name + "do something2!";
    }
}
package com.DesignPatterns.al.Dynamic92;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import sun.misc.ProxyGenerator;

/**
 * @Author: feiweiwei
 * @Description:
 * @Created Date: 10:00 17/9/22.
 * @Modify by:
 */
public class DynamicProxyMain {

    public static void main(String[] args) throws IOException {
        // 目標類1
        Subject realSubject = new RealSubject();
        // 處理器類
        InvocationHandler handler = new DynamicProxyAOP(realSubject);

        Subject subject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject
                .getClass().getInterfaces(), handler);
        subject.doSomething("tester ");
        /*
         * // 目標類2 Subject realSubject2 = new RealSubject2();
         */

        // 多型指向DynamicProxyAOP的介面類InvocationHandler

        // 多型指向DynamicProxyAOP的介面類InvocationHandler
        /* InvocationHandler handler2 = new DynamicProxyAOP(realSubject2); */

        /*
         * Subject subject2 = (Subject) Proxy.newProxyInstance(realSubject2.getClass().getClassLoader(), realSubject2
         * .getClass().getInterfaces(), handler2);
         */

        // subject2.doSomething("tester ");

        String path = "E:/$Proxy0.class";
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", RealSubject.class.getInterfaces());
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(path);
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

 

 

生成的代理類

import com.DesignPatterns.al.Dynamic92.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0
  extends Proxy
  implements Subject
{
  private static Method m1;
  private static Method m3;
  private static Method m0;
  private static Method m2;
  
  public $Proxy0(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 doSomething(String paramString)
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m3, new Object[] { paramString });
    }
    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);
    }
  }
  
  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);
    }
  }
  
  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.DesignPatterns.al.Dynamic92.Subject").getMethod("doSomething", new Class[] { Class.forName("java.lang.String") });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

 

控制檯:

before do something
tester  do something!
after do something

 

是不是很像啊。

有興趣的可以研究一下它的原始碼

下面是原始碼的連結文件:

https://blog.csdn.net/sum__mer/article/details/53179662
https://www.jianshu.com/p/269afd0a52e6
https://www.jianshu.com/p/471c80a7e831

 kk