設計模式三: 代理模式(Proxy) -- JDK的實現方式

簡介

代理模式屬於行爲型模式的一種, 控制對其餘對象的訪問, 起到中介做用.java

代理模式核心角色: 真實角色,代理角色;app

按實現方式不一樣分爲靜態代理和動態代理兩種;框架

意圖

控制對其它對象的訪問。ide

類圖

Proxy模式

實現

JDK自帶了Proxy的實現, 下面咱們先使用JDK的API來演示代理如何使用, 隨後再探究Proxy的實現原理,並本身來實現Proxy.學習

JDK代理類的使用: (InvocationHandler,Proxy)

使用JDK實現的代理代碼以下, 先定義業務接口`Car`,而後實現該接口`QQCar`,實現類即爲真實角色. 繼續定義代理類`Agent`,代理類須要實現接口`InvocationHandler`的`invoke()`方法, 最後是調用;
    注意代理類使用了`Proxy.newProxyInstance()`方法動態生成代理對象, 在稍後手寫代碼時咱們將參考本方法定義咱們本身的實現方式.
/**
 * 汽車接口,定義業務規範
 */
public interface Car {
    void sale();
}
/**
 * 接口Car的具體實現,是代理模式中的真實角色
 */
public class QQCar implements Car {
    public void sale() {
        System.out.println("賣了一輛qq");
    }
}
/**
 * 經紀公司,或者經銷商
 */
public class Agent implements InvocationHandler {

    private Car car ;

    /**
     * 返回代理對象,接收被代理對象
     * @param car
     * @return
     * @throws Exception
     */
    public Object getInstance(Car car) throws Exception {
        this.car=car;
        Class clazz = car.getClass();
        // 看下代理先後的具體類型
        System.out.println("代理前對象的類型"+car.getClass().getName());
        Object obj = Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
        // 看下代理先後的具體類型
        System.out.println("代理後對象類型變爲"+obj.getClass().getName());
        return obj;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Agent find some costumers");
        //this.car.sale();
        method.invoke(this.car, args);
        System.out.println("Agent saled the car");
        return null;
    }
}
try {
    Car car = (Car) new Agent().getInstance(new QQCar());
    car.sale();
}catch (Exception e){
    e.printStackTrace();
}

總結JDK原理以下:this

  1. 獲取真實角色對象的引用並獲取其接口
  2. Proxy生成一個代理類,並實現接口的方法
  3. 獲取被代理對象的引用
  4. 動態生成代理類的class字節碼
  5. 編譯加載

手寫實現Proxy

要本身實現JDK的代理模式,咱們首先要搞清楚JDK的Proxy是如何實現的, 經過調試及查看源碼能夠知道JDK生成了一個$Proxy0的類型,咱們也將該類型名稱打印到了控制檯. 若是能動態生成,編譯並將這個類加載到內存, 咱們就能夠本身實現Proxy了.spa

  1. 首先拿到$Proxy0的代碼,供咱們後面生成代理類的源碼時參考
//生產接口Car對應的代理類class文件並保存到文件
byte[] data = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Car.class});
FileOutputStream outputStream = new FileOutputStream("E:\\design-patterns\\src\\main\\java\\com\\xlx\\pattern\\proxy\\jdk\\$Proxy0.class");
outputStream.write(data);
outputStream.close();

生成的$Proxy0.class文件反編譯後的代碼以下, 能夠看到其實現了Car接口.代理

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.xlx.pattern.proxy.jdk.Car;
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 Car {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void sale() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.xlx.pattern.proxy.jdk.Car").getMethod("sale");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
  1. 參考JDK的實現,咱們分別定義MyClassLoader,MyInvocationHandler,MyProxy,分別對應ClassLoader,InvocationHandler,Proxy; 其中接口MyInvocationHandler代碼以下:
/**
 * 代理類須要實現該接口
 */
public interface MyInvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
  1. 定義代理類MyAgent對應咱們使用JDK時定義的Agent, 但getInstance()方法實現所有改成2中自定義的類,實現2中定義的接口
/**
 * 代理類
 */
public class MyAgent implements MyInvocationHandler {

    private Car car;

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Agent find some costumers");
        //this.car.sale();
        method.invoke(this.car,args);
        System.out.println("Agent saled the car");
        return null;
    }

    public Object getInstance(Car car) throws Exception {
        this.car=car;
        Class clazz = car.getClass();
        Object obj = MyProxy.newProxyInstance(new MyClassLoader(),clazz.getInterfaces(),this);
        return obj;
    }
}
  1. 實現MyProxy中生成代理對象的方法newProxyInstance() 具體又分爲如下幾步:調試

    1. 定義動態代理類的源碼
     2. 保存源碼文件到磁盤
     3. 編譯源碼文件爲.class文件
     4. 加載.class字節碼到內存 (具體實現見5.實現MyClassLoader))
     5. 返回代理對象
/**
 * 生成代理對象的代碼, Proxy的具體原理在這裏體現
 */
public class MyProxy {

    private static final String ln = "\r\n";

    public static Object newProxyInstance(MyClassLoader loader, Class<?>[] interfaces, MyInvocationHandler h) {
        File f = null;
        try {
            // 第一步: 生成源代碼
            String src = generateSrc(interfaces[0]);

            // 第二步: 保存生成的源碼文件
            String filePath = MyProxy.class.getResource("").getPath();
            f = new File(filePath + "/$Proxy0.java");
            FileWriter writer = new FileWriter(f);
            writer.write(src);
            writer.flush();
            writer.close();

            // 第三步: 編譯生成.class文件
            JavaCompiler compliler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = compliler.getStandardFileManager(null, null, null);
            Iterable iterable = manager.getJavaFileObjects(f);
            JavaCompiler.CompilationTask task = compliler.getTask(null, manager, null, null, null, iterable);
            ((JavaCompiler.CompilationTask) task).call();
            manager.close();

            // 第四步: 加載class字節碼到內存(MyClassLoader類實現)
            Class proxyClass = loader.findClass("$Proxy0");
            // 第五步: 返回代理對象
            Constructor constructor = proxyClass.getConstructor(MyInvocationHandler.class);
            return constructor.newInstance(h);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != f) {
                f.delete();
            }
        }
        return null;
    }

    /**
     * 生成源碼的方法
     *
     * @param interfaces 爲了演示,按一個接口處理
     * @return
     */
    private static String generateSrc(Class<?> interfaces) {
        StringBuffer src = new StringBuffer();
        src.append("package com.xlx.pattern.proxy.my;" + ln);
        src.append("import java.lang.reflect.Method;" + ln);
        src.append("public class $Proxy0 extends MyProxy implements " + interfaces.getName() + "{" + ln);
        src.append("MyInvocationHandler h;" + ln);

        src.append("public $Proxy0(MyInvocationHandler h){" + ln);
        src.append("this.h=h;" + ln);
        src.append("}" + ln);

        // 循環定義方法,與被代理類的方法同名
        for (Method m : interfaces.getMethods()) {
            src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln);

            src.append("try{" + ln);
            src.append("Method m =" + interfaces.getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln);
            src.append("this.h.invoke(this,m,null);" + ln);
            src.append("}catch(Throwable e){e.printStackTrace();}" + ln);
            src.append("}" + ln);
        }

        src.append("}" + ln);
        return src.toString();
    }
}
  1. 實現MyClassLoaderfindClass()方法,最終由父類ClassLoader.defineClass()方法加載,完成最後拼圖
/**
 * 代碼生成,編譯,從新加載到內存
 * 類加載器, 使用ClassLoader
 */
public class MyClassLoader extends ClassLoader{

    File basePath ;

    public MyClassLoader(){
        String basePath = MyClassLoader.class.getResource("").getPath();
        this.basePath = new File(basePath) ;
    }

    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException{

        String className = MyClassLoader.class.getPackage().getName()+"."+name;

        if (null!=basePath){
            File classFile = new File(basePath,name.replaceAll("\\.","/")+".class");
            if (classFile.exists()){
                FileInputStream in = null;
                ByteArrayOutputStream out= null;
                try {
                    in = new FileInputStream(classFile);
                    out = new ByteArrayOutputStream();
                    byte[] buffer = new byte[1024];
                    int len;
                    while ((len=in.read(buffer))!=-1){
                        out.write(buffer,0,len);
                    }
                    return defineClass(className,out.toByteArray(),0,out.size());
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    classFile.delete();
                    if (null!=in){
                        try{
                            in.close();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                    if (null!=out){
                        try{
                            out.close();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

        return null;
    }
}

總結

上面我仿照JDK自帶API用本身的代碼實現了Proxy, 這樣寫了一次以後對Proxy的實現原理加深了不少.Proxy做爲一種重要的模式已經大量用在了目前流行的不少框架上, 理解了原理就更有信心去學習框架以及框架的實現思想了.code

優勢: 1. 職責清晰,真實角色專一實現業務邏輯,代理角色去完成具體的事務,代碼結構簡潔清晰;2. 可擴展

補充

上面研究了JDK動態代理的實現, 首先定義了接口,而後用一個類實現這個接口,這個實現類就是要代理的具體對象;

cglib庫也實現了Proxy模式,與JDK不一樣的是, cglib不須要定義接口, 而是經過生成被代理類的子類來實現代理模式.這使得代理模式的使用更加簡單. 通常類型都可以做爲被代理類型.

大體的實現以下(原理跟jdk實現差很少,只不過cglib使用的是類繼承實現):

/**
 * 演示 cglib 代理方式
 */
public class CGAgent implements MethodInterceptor {

    public Object getInstance(Class clazz) throws Exception{
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理開始了....");
        methodProxy.invokeSuper(o,objects);
        System.out.println("代理結束了....");
        return null;
    }
}
相關文章
相關標籤/搜索