设计模式(1)--动态代理(Dynamic Proxy)

笔者写这篇blog来检验下自己对于设计模式的学习成果,另一目的也是来分享经验和各位交流交流。
学设计模式主要学的是设计的思想,而不是单纯地记住具体的实现,粗浅地说就是先要搞清楚为什么要这么设计(由什么实际需求引导了这种设计的产生,属于问题导向),接着才是如何具体地实现这种设计。
初学设计不必要担心设计上的好与不好,先得积累经验,再逐步优化,这不同于学习框架,以后者来说就是学会配置和使用先(因为这是主干),再细究底层的实现原理。比较起来两者的区别是:

  • 设计模式学习:先做思想工作,再做实际操作;独立于编程的,可以不用变化的(设计思想)
  • 框架学习:先做实际操作,再做思想工作;不能脱离编程的,随着版本的变化可能会随时改变的(配置和配置方式)

这也是笔者先学来的方法论。

1.Flow-process diagram

Flow-process diagram注:流程图在线制作工具draw.io
如上图所示,笔者今天要做的主要工作是先使用Inheritance(继承)和Aggregation(聚合)等static proxy(静态代理)的模式,其实严格来说,Inheritance不太算是static proxy,因为它应当属于polymorphism(多态)的一种类似代理的实现。
现在再说说笔者对于代理模式的理解:当前对象实现了method a,但是需要在执行method a的前、后需要进行一些其他操作的时候我们就需要一个proxy来替我们来做这些工作,再者代理主要是帮我们做好了前后的处理,然而method a的调用还是要靠这个当前对象来完成。逻辑图如下:
这里写图片描述
注:pre-handle & after-handle不是必要的。

2.Basic code

Writable.java

package com.unicorn.test;

/**
 * @author Unicorn
 * @descrition 所有能够进行书写的实物的最高抽象接口
 * @created 2018-04-26-20:58
 */
public interface Writable {
    /**
     * @description 书写能力
     * @param
     * @author Unicorn
     * @date 2018-04-09 21:00:53
     */
    public abstract void write();
}

Note.java

package com.unicorn.test;

import java.util.Random;

/**
 * @author Unicorn
 * @descrition 笔记本类, 需要实现Writable接口
 * @created 2018-04-26-21:01
 */

public class Note implements Writable{

    public Note() {

    }

    @Override
    public void write() {
        System.out.println("我打算写点什么东西来刷下我的存在感!");
        //模拟休眠
        try {
            Thread.sleep(new Random().nextInt(500));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

这里主要是基础的代码,笔者创建了一个接口,并通过一个类来实现了这个接口,接下来才是我们的重点。

3.Static Proxy

3.1 Aggregation

NoteRunningTimeProxy.java

package com.unicorn.test;

/**
 * @author Unicorn
 * @descrition Aggregation
 * @created 2018-04-26-21:22
 */

public class NoteRunningTimeProxy implement Writable{

    private Note n;

    public NoteRunningTimeProxy(Note n) {
        this.n = n;
    }

    @Override
    public void write() {
        //处理前的验证
        long start = System.currentTimeMillis();
        System.out.println("开始写作!");
        this.n.write();
        //处理后的收尾
        long end = System.currentTimeMillis();
        System.out.println("一共写了:" + (end - start) + "毫秒");
    }
}

3.2 Inheritance

NoteRunningTimeProxy2.java

package com.unicorn.test;

/**
 * @author Unicorn
 * @descrition 使用继承的方式实现静态代理
 *             重载父类的方法来实现方法的修饰
 * @created 2018-04-26-21:22
 */

public class NoteRunningTimeProxy2 extends Note{

    public NoteRunningTimeProxy2() {

    }

    @Override
    public void write() {
        //这是使用继承实现的静态的代理
        //处理前的验证
        long start = System.currentTimeMillis();
        System.out.println("开始写作!");
        super.write();
        //处理后的收尾
        long end = System.currentTimeMillis();
        System.out.println("一共写了:" + (end - start) + "毫秒");
    }
}

可以看到这两种代理的方式其实是差不多的。

  1. Aggregation的实现方式是低耦合的
  2. Inheritance的实现是polymorphism(多态)的,属于高耦合

这里笔者谈谈自己对于耦合的粗浅理解:Class b中的代码与Class a的代码存在一定的关系,包括:通过类的实例化对象调用类中的方法、方法的重写/覆盖(超类对基类的方法进行覆盖,即基类引用指向超类对象时所调用的基类方法会默认去调用超类对此方法的实现)等等,这就是耦合。
现在假设a为基类,b为超类,那么一般a和b之间更多的时候就会存在@Override(重写/覆盖)的关系,一旦基类的发生改变,超类一定要跟着做相应的变化,否者无法编译,因为这种关系时要在编译期间决定的(多态是编译期就要完成的,包括extends和implements,这两个中前者是继承一个类或抽象类,后者是实现某个接口/完全抽象类),那么这就属于高耦合中的一种情况。(又称继承/实现)
那么现在情况改变下,假设a和b之间不是超类和基类之间继承的关系,而单纯是后者为前者一个属性,这样类和类之间的耦合性就会大大地减少,而且这样的关系是在编译后的运行期间才会确立,尤其是当b为一个接口的时候,我们传入一个实现它接口的实例化对象(构造器传入,setter传入),那么只有在运行期间才会决定调用那个对象的方法。(也称关联,聚合和组合)。
像上面的两种static proxy都其实不好用,原因是:
1. 需要人工编写大量的代码
2. 需要人工维护类和类之间的关系,特别容易产生数量膨胀的类

上面的代码这么狗血干嘛还用呢?为什么我们不能搞点灵活的东西出来?但是我们该怎么搞?如果我们能够对上面的代码做些sao操作就好了!……((/- -)/
如果你是这么想的,这可6大发了,至少比笔者刚学的时候要6,6在有点方向感!
接下来才是重头戏!

4.The travel to debug

4.1 Some thoughts

根据刚才的方向,笔者现在想法是把NoteRunningTimeProxy.java中的整个类代码变成动态生成的,这样就不就成了动态的代理了么!
要生成动态的代码必须有源文件,接着源文件必须进行编译,最后加载到内存中。
现在来说说解决方案:

  • 源文件生成:使用File类,把NoteRunningTimeProxy.java中的整个类代码变成字符串写入到NoteRunningTimeProxy.java file中,也就是说我们抛弃了刚刚的NoteRunningTimeProxy.java源文件,通过文件流的方式来生成。
  • 编译:我们曾经使用的命令行javac不就是来编译的吗?它在我们安装的jdk中,我们能不能拿到它并在idea中使用。一番javax查找后别说还真有,它叫做JavaCompiler。
    这里写图片描述
  • 加载到内存中:编译好之后的文件肯定是.class,那么我们就可以使用反射机制了。

参考:
- Deep learning about Java–贯穿Java的反射机制(1)
- Deep learning about Java–贯穿Java的反射机制(2)
- https://blog.fondme.cn/apidoc/jdk-1.8-google/

4.2 codes version1.0

笔者现在设置一个Proxy,管理NoteRunningTimeProxy.java的动态生成,并更名为NoteProxy避免类冲突。
Proxy.java

package com.unicorn.test;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * @author Unicorn
 * @descrition 动态代理类
 * @created 2018-04-26-22:20
 */

public class Proxy {
    public static Object newInstance(Note note){

        String src = "package com.unicorn.test;\n" +
                "\n" +
                "public class NoteProxy implements Writable{\n" +
                "\n" +
                "    private Note n;\n" +
                "\n" +
                "    public NoteProxy(Note n) {\n" +
                "        this.n = n;\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public void write() {\n" +
                "       long start = System.currentTimeMillis();\n" +
                "       System.out.println(\"开始写作!\");\n" +
                "        n.write();\n" +
                "        long end = System.currentTimeMillis();\n" +
                "        System.out.println(\"一共写了:\" + (end - start) + \"毫秒\");\n" +
                "    }\n" +
                "}\n";

        //创建.java文件
        String fileName = "H:/src/com/unicorn/test/NoteProxy.java";
        File f = new File(fileName);
        FileWriter fw = null;
        try {
           fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //使用Javac编译程binary文件
        JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager mgr = jc.getStandardFileManager(null, null, null);
        Iterable units = mgr.getJavaFileObjects(fileName);
        JavaCompiler.CompilationTask task= jc.getTask(null, mgr, null, null, null, units);
        task.call();
        try {
            mgr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //加载编译后的.class文件并创建一个对象返回
        URL[] urls = null;
        URLClassLoader ucl = null;
        Class clazz = null;
        Constructor ctr = null;
        Object obj = null;
        try {
            urls = new URL[]{new URL("file:/H:/src/")};
            ucl = new URLClassLoader(urls);
            //这里已经可以把该类加载到内存中了
            clazz = ucl.loadClass("com.unicorn.test.NoteProxy");
            System.out.println(clazz);
            ctr = clazz.getConstructor(Note.class);
            System.out.println(ctr.getName());
            obj = ctr.newInstance(note);
            System.out.println("test: " + obj);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return obj;
    }
}

经过对上面的测试,它已经是可以实现笔者前面的三个要求了,然而事情到这还远远没有解决!
我们想象一下我们这样就只能实现对Note类的代理,不能对其它实现了Writable的类进行代理,那搞那么多东西不是白费力气吗?那接着进入下一版本的优化。

4.3 codes version1.1

要解决上面的问题,其实只要换成接口就好,也就是使用多态的思想。
Proxy.java

package com.unicorn.test;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * @author Unicorn
 * @descrition 动态代理类
 * @created 2018-04-26-22:20
 */

public class Proxy {
    public static Object newInstance(Writable w){

        String src = "package com.unicorn.test;\n" +
                "\n" +
                "public class NoteProxy implements Writable{\n" +
                "\n" +
                "    private Writable w;\n" +
                "\n" +
                "    public NoteProxy(Writable w) {\n" +
                "        this.w = w;\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public void write() {\n" +
                "       long start = System.currentTimeMillis();\n" +
                "       System.out.println(\"开始写作!\");\n" +
                "        w.write();\n" +
                "        long end = System.currentTimeMillis();\n" +
                "        System.out.println(\"一共写了:\" + (end - start) + \"毫秒\");\n" +
                "    }\n" +
                "}\n";

        //创建.java文件
        String fileName = "H:/src/com/unicorn/test/NoteProxy.java";
        File f = new File(fileName);
        FileWriter fw = null;
        try {
           fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //使用Javac编译程binary文件
        JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager mgr = jc.getStandardFileManager(null, null, null);
        Iterable units = mgr.getJavaFileObjects(fileName);
        JavaCompiler.CompilationTask task= jc.getTask(null, mgr, null, null, null, units);
        task.call();
        try {
            mgr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //加载编译后的.class文件并创建一个对象返回
        URL[] urls = null;
        URLClassLoader ucl = null;
        Class clazz = null;
        Constructor ctr = null;
        Object obj = null;
        try {
            urls = new URL[]{new URL("file:/H:/src/")};
            ucl = new URLClassLoader(urls);
            //这里已经可以把该类加载到内存中了
            clazz = ucl.loadClass("com.unicorn.test.NoteProxy");
            System.out.println(clazz);
            ctr = clazz.getConstructor(Writable.class);
            System.out.println(ctr.getName());
            obj = ctr.newInstance(w);
            System.out.println("test: " + obj);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return obj;
    }
}

到了这我们该高兴吗?No!No!No!这才走了不到一半的路啊!
再提出一个问题,这是一个只能实现write method,如果Writable里还有其他的方法接口怎么办?

4.4 codes version1.2

又是一个绝望的问题!笔者默默地在地上画个圈圈!
幸好我们可以使用Method的反射机制获得方法对象!( ̄_, ̄ )
Proxy.java

package com.unicorn.test;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * @author Unicorn
 * @descrition 动态代理类
 * @created 2018-04-26-22:20
 */

public class Proxy {
    public static Object newInstance(Writable w){

        Method[] methods = w.getClass().getDeclaredMethods();
        String methodSrc = "";
        for (Method m : methods) {
            methodSrc +=
                    "    @Override\n" +
                    "    public void "+ m.getName() +"() {\n" +
                    "       long start = System.currentTimeMillis();\n" +
                    "       System.out.println(\"开始写作!\");\n" +
                    "       m.invoke(w)\n" +
                    "       long end = System.currentTimeMillis();\n" +
                    "       System.out.println(\"一共写了:\" + (end - start) + \"毫秒\");\n" +
                    "    }\n";
        }

        String src = "package com.unicorn.test;\n" +
                "\n" +
                "public class NoteProxy implements Writable{\n" +
                "\n" +
                "    private Writable w;\n" +
                "\n" +
                "    public NoteProxy(Writable w) {\n" +
                "        this.w = w;\n" +
                "    }\n" +
                "\n" +
                methodSrc +
                "}\n";

        //创建.java文件
        String fileName = "H:/src/com/unicorn/test/NoteProxy.java";
        File f = new File(fileName);
        FileWriter fw = null;
        try {
           fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //使用Javac编译程binary文件
        JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager mgr = jc.getStandardFileManager(null, null, null);
        Iterable units = mgr.getJavaFileObjects(fileName);
        JavaCompiler.CompilationTask task= jc.getTask(null, mgr, null, null, null, units);
        task.call();
        try {
            mgr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //加载编译后的.class文件并创建一个对象返回
        URL[] urls = null;
        URLClassLoader ucl = null;
        Class clazz = null;
        Constructor ctr = null;
        Object obj = null;
        try {
            urls = new URL[]{new URL("file:/H:/src/")};
            ucl = new URLClassLoader(urls);
            //这里已经可以把该类加载到内存中了
            clazz = ucl.loadClass("com.unicorn.test.NoteProxy");
            System.out.println(clazz);
            ctr = clazz.getConstructor(Writable.class);
            System.out.println(ctr.getName());
            obj = ctr.newInstance(w);
            System.out.println("test: " + obj);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return obj;
    }
}

又一次解决问题,觉得自己很pang?Too young, too simple!(ˉ▽ ̄~) 切~~
如果有需求要把pre-handle和after-handle独立出来还有的你哭的。

4.5 codes version1.3

笔者为了证明自己不是个二愣子,又进入了修改的路途中。
分析一下我们需求其实不难发现这其实就是代码复用的问题,只要把相应的代码提取出来独立成一个class就好了。
现在笔者把它独立出来成为一个InvocationHandler.java,专门来处理method decoration的问题,同时为了刚好地复用代码,笔者把它设计为一个接口。
InvocationHandler.java

package com.unicorn.test;

import java.lang.reflect.Method;

/**
 * @author Unicorn
 * @descrition 方法调用处理器, 主要是要把修饰放在这构成一个方法,
 * 再通过反射机制和compile编译成的.class文件对该方法进行调用
 * @created 2018-04-28-0:07
 */
public interface InvocationHandler {
    public void invoke(Method m);
}

为了使用这个接口,我们就需要implement一下,创建一个专门处理Note method decoration的handler.
NoteWriteInvocationHandler.java

package com.unicorn.test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author Unicorn
 * @descrition
 * @created 2018-04-28-0:11
 */

public class NoteWriteInvocationHandler implements InvocationHandler{
    private Writable w;

    public NoteWriteInvocationHandler(Writable w) {
        this.w = w;
    }

    @Override
    public void invoke(Method m) {
        long start = System.currentTimeMillis();
        System.out.println("开始写作!");
        try {
            m.invoke(this.w);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("一共写了:" + (end - start) + "毫秒");
    }
}

Proxy.java

package com.unicorn.test;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * @author Unicorn
 * @descrition 动态代理类
 * @created 2018-04-26-22:20
 */

public class Proxy {
    public static Object newInstance(InvocationHandler invoc){

        Method[] methods = w.getClass().getDeclaredMethods();
        String methodSrc = "";
        for (Method m : methods) {
            methodSrc +=
                    "    @Override\n" +
                    "    public void "+ m.getName() +"() {\n" +
                    "    invoc.invoke(m);\n" +
                    "    }\n";
        }

        String src = "package com.unicorn.test;\n" +
                "import java.lang.reflect.Method;\n" +
                "public class NoteProxy implements Writable{\n" +
                "\n" +
                "    private com.unicorn.test.InvocationHandler invoc;\n" +
                "\n" +
                "    public NoteProxy(com.unicorn.test.InvocationHandler invoc) {\n" +
                "        this.invoc = invoc;\n" +
                "    }\n" +
                "\n" +
                methodSrc +
                "}\n";

        //创建.java文件
        String fileName = "H:/src/com/unicorn/test/NoteProxy.java";
        File f = new File(fileName);
        FileWriter fw = null;
        try {
           fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //使用Javac编译程binary文件
        JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager mgr = jc.getStandardFileManager(null, null, null);
        Iterable units = mgr.getJavaFileObjects(fileName);
        JavaCompiler.CompilationTask task= jc.getTask(null, mgr, null, null, null, units);
        task.call();
        try {
            mgr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //加载编译后的.class文件并创建一个对象返回
        URL[] urls = null;
        URLClassLoader ucl = null;
        Class clazz = null;
        Constructor ctr = null;
        Object obj = null;
        try {
            urls = new URL[]{new URL("file:/H:/src/")};
            ucl = new URLClassLoader(urls);
            //这里已经可以把该类加载到内存中了
            clazz = ucl.loadClass("com.unicorn.test.NoteProxy");
            System.out.println(clazz);
            ctr = clazz.getConstructor(com.unicorn.test.InvocationHandler.class);
            System.out.println(ctr.getName());
            obj = ctr.newInstance(invoc);
            System.out.println("test: " + obj);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return obj;
    }
}

真的,是不是有种又一次完成的艰巨的任务的错觉,然而各位是不是隐隐感觉到后面还有大坑!对的,到现在为止我们仍然脱离不了只是解决一类接口的代理,如果我们把Proxy个InvocationHandler完全变成一个通用的代理怎么办?

4.6 codes version1.4

怎么办?想呗!
假如我们能够获得传入的类的名称不久好了么!反正是动态代理,在运行期间才会正确获取对象的信息,对,这么干!
由于InvocationHandler是一个接口,不可能让我们进行成员属性的设置,那么我们约定下实现这个接口的规范:

  • 必须添加成员Object作为复用的关键,即作为接收任意具体的对象
  • method invoke内做method decoration
  • Proxy.newInstance中传入接口类,包括具体所需要代理的接口类以及InvocationHandler对应的实现类
  • 代理类的名称约定为$Proxy1

    这里写图片描述

NoteWriteInvocationHandler.java

package com.unicorn.test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author Unicorn
 * @descrition
 * @created 2018-04-28-0:11
 */

public class NoteWriteInvocationHandler implements InvocationHandler{
    private Object obj;

    public NoteWriteInvocationHandler(Object obj) {
        this.obj = obj;
    }

    @Override
    public void invoke(Method m) {
        //pre-handle
        long start = System.currentTimeMillis();
        System.out.println("开始写作!");
        try {
            m.invoke(this.obj);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        //after-handle
        long end = System.currentTimeMillis();
        System.out.println("一共写了:" + (end - start) + "毫秒");
    }
}

Proxy.java

package com.unicorn.test;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * @author Unicorn
 * @descrition 动态代理类
 * @created 2018-04-26-22:20
 */

public class Proxy {
    public static Object newInstance(Class c, InvocationHandler invoc){

        Method[] methods = c.getDeclaredMethods();
        String methodSrc = "";
        for (Method m : methods) {
            methodSrc +=
                    "    @Override\n" +
                            "    public void "+ m.getName() +"() {\n" +
                            "       try{\n"+
                            "       Method m = " + c.getSimpleName() + ".class.getMethod(\""+ m.getName() +"\");\n" +
                            "       invoc.invoke(m);\n" +
                            "       } catch(Exception e) {\n"+
                            "       e.printStackTrace();\n"+
                            "       }\n" +
                            "    }\n";
        }

        String src = "package com.unicorn.test;\n" +
                "import java.lang.reflect.Method;\n" +
                "public class $Proxy1 implements "+ c.getSimpleName() +"{\n" +
                "\n" +
                "    private com.unicorn.test.InvocationHandler invoc;\n" +
                "    public $Proxy1(com.unicorn.test.InvocationHandler invoc) {\n" +
                "        this.invoc = invoc;\n" +
                "    }\n" +
                "\n" +
                methodSrc +
                "}\n";


        //创建.java文件
        String fileName = "H:/src/com/unicorn/test/$Proxy1.java";
        File f = new File(fileName);
        FileWriter fw = null;
        try {
           fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //使用Javac编译程binary文件
        JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager mgr = jc.getStandardFileManager(null, null, null);
        Iterable units = mgr.getJavaFileObjects(fileName);
        JavaCompiler.CompilationTask task= jc.getTask(null, mgr, null, null, null, units);
        task.call();
        try {
            mgr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //加载编译后的.class文件并创建一个对象返回
        URL[] urls = null;
        URLClassLoader ucl = null;
        Class clazz = null;
        Constructor ctr = null;
        Object obj = null;
        try {
            urls = new URL[]{new URL("file:/H:/src/")};
            ucl = new URLClassLoader(urls);
            //这里已经可以把该类加载到内存中了
            clazz = ucl.loadClass("com.unicorn.test.$Proxy1");
            System.out.println(clazz);
            ctr = clazz.getConstructor(com.unicorn.test.InvocationHandler.class);
            System.out.println(ctr.getName());
            obj = ctr.newInstance(invoc);
            System.out.println("test: " + obj);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return obj;
    }
}

Test2.java

package com.unicorn.test;

import java.util.InvalidPropertiesFormatException;

/**
 * @author Unicorn
 * @descrition 测试自己实现的动态代理
 * @created 2018-04-26-22:22
 */

public class Test2 {
    public static void main(String[] args) {
        InvocationHandler invoc = new NoteWriteInvocationHandler(new Note());
        Writable w = (Writable) Proxy.newInstance(Writable.class, invoc);
        w.write();
    }
}

大功告成!运行一下!
这里写图片描述
这里的逻辑是层层的类包装,最终创建一个代理类的对象,其内部加上了对调用方法的pre-handle & after-handle的修饰,通过这个代理类的对象去完成被代理类应该做的事情。
我们可以再通过一个类来检验是否真正完成了dynamic proxy。

4.7 check

Moveable.java

package com.unicorn.test;

/**
 * @author Unicorn
 * @descrition 可移动的接口
 * @created 2018-04-28-9:56
 */
public interface Moveable {
    public abstract void go();
}

Bicycle.java

package com.unicorn.test;

/**
 * @author Unicorn
 * @descrition 实现Moveable接口
 * @created 2018-04-28-10:08
 */

public class Bicycle implements Moveable{

    @Override
    public void go() {
        System.out.println("今天骑着单车唱着陈奕迅的《单车》");
    }
}

BicycleInvocationHandler.java

package com.unicorn.test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Random;

/**
 * @author Unicorn
 * @descrition bicycle的方法处理器
 * @created 2018-04-28-10:10
 */

public class BicycleInvocationHandler implements InvocationHandler {
    private Object obj;

    public BicycleInvocationHandler(Object obj) {
        this.obj = obj;
    }

    @Override
    public void invoke(Method m) {
        System.out.println("解锁单车");
        long start = System.currentTimeMillis();
        try {
            m.invoke(obj);
            Thread.sleep(new Random().nextInt(50));
        } catch (Exception e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("今天骑了" + (end - start) +"km");

    }
}

Test2.java

package com.unicorn.test;

import java.util.InvalidPropertiesFormatException;

/**
 * @author Unicorn
 * @descrition 测试自己实现的动态代理
 * @created 2018-04-26-22:22
 */

public class Test2 {
    public static void main(String[] args) {
        InvocationHandler invoc = new NoteWriteInvocationHandler(new Note());
        Writable w = (Writable) Proxy.newInstance(Writable.class, invoc);
        w.write();

        //bicycle
        InvocationHandler invoc2 = new BicycleInvocationHandler(new Bicycle());
        Moveable m = (Moveable) Proxy.newInstance(Moveable.class, invoc2);
        m.go();
    }
}

这里写图片描述
谢谢啊!笔者终于写完了!
笔者敢打赌,能看到这的人绝对不多!
笔者再这还要宣布一个坏消息:上面的dynamic proxy还是有缺陷的!╰( ̄▽ ̄)╭
哈哈!是不是又被坑了!它的缺陷是代理时的pre-handle和after-handle只能是一样的!
不管了,笔者这次就i到此为止了!
感谢各位的支持!这次的blog是真的难写( -‘`-; )!因为不像配置类的blog按着流程给你布置好就行了,这设计模式还的说点干货!
如果note有错漏,恳请各位留下评论指出,笔者会及时改正!
谢谢!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值