前言

【设计模式】——代理模式(静态)以及【设计模式】——代理模式(动态)中,我们已经基本了解了什么是代理模式,以及什么是静态代理,什么是动态代理。

DK动态代理以及Cglib动态代理其实底层实现原理都是字节码的重组,不过各自对应的代理场景不同,本文我们重点研究jdk动态代理。

通过前文的了解,我们已经知道在JDK动态代理中是JDK动态的帮我们生成一个名为$Proxy0的代理类,那么,我们本文来纯手写实现JDK动态代理,也就是我们自己生成这个代理类,与JDK动态代理的思想一致,只为简化,细节部分本文并未过多研究。

实现

在前文动态代理实现jdk动态代理中我们有这样一个类的调用关系图:
20190822150752270.png

其中需要引用到jdk的Proxy和InvocationHandler这两个类,这里我们自己简单实现MyProxy.java和MyInvocationHandler.java

由于需要动态生成代理类,那么就需要生成,编译,加载到jvm,因此我们实现了一个简单的类加载器MyClassLoader.java

此时类调用关系图略微变化:
20190822155245897.png

不多说,上代码,大多地方都有比较详细的注释

MyInvocationHandler.java

/** 用于自定义代理逻辑处理
 1. @author WangZhiJun
 */
public interface MyInvocationHandler {
    /** invoke
     * @param proxy 指被代理的对象
     * @param method 要调用的方法
     * @param args 方法调用时所需要的参数
     * @return Object
     * @throws Throwable 异常
     */
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}


MyProxy.java: 这个类就是JDK动态代理的关键,其中进行了代理类的动态生成:

  1. 生成源代码
  2. 将生成的源代码输出到磁盘,保存为.java文件
  3. 编译源代码,并且生成.class文件
  4. 将class文件中的内容,动态加载到JVM中来
  5. 返回被代理后的代理对象

MyProxy.java

/** 生成代理对象的代码
 * @author WangZhiJun
 */
class MyProxy {
 
    private static final String ln = "\r\n";
 
    /** 通过此类为一个或多个接口动态的生成实现类
     * @param classLoader 类加载器
     * @param interfaces 得到全部的接口
     * @param h 得到InvocationHandler接口的子类实例
     * @return Object
     */
    static Object newProxyInstance(MyClassLoader classLoader, Class<?>[] interfaces, MyInvocationHandler h){
        try{
            //1、生成源代码
            String proxySrc = generateSrc(interfaces[0]);
            //2、将生成的源代码输出到磁盘,保存为.java文件
            String filePath = MyProxy.class.getResource("").getPath();
            File f = new File(filePath + "$Proxy0.java");
            FileWriter fw = new FileWriter(f);
            fw.write(proxySrc);
            fw.flush();
            fw.close();
            //3、编译源代码,并且生成.class文件
            JavaCompiler  compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
            Iterable iterable = manager.getJavaFileObjects(f);
            CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
            task.call();
            manager.close();
            //4.将class文件中的内容,动态加载到JVM中来
            //5.返回被代理后的代理对象
            Class proxyClass = classLoader.findClass("$Proxy0");
            Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);
            //这里先不删除生成的$Proxy0.java文件,实际上要删除的
            f.delete();
            return c.newInstance(h);
 
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /** 生成代理对象$Proxy0的源代码
     * @param interfaces 抽象对象
     * @return String
     */
    private static String generateSrc(Class<?> interfaces){
        StringBuilder src = new StringBuilder();
        src.append("package com.wang.proxy.custom.jdk.proxy;" + ln);
        //引入反射相关的包
        src.append("import java.lang.reflect.Method;" + ln);
        //动态代理类实现被代理接口,在此为Person类
        src.append("public class $Proxy0 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("}");
        return src.toString();
    }
}

MyClassLoader.java

/**将class重新动态load到JVM
 * @author WangZhiJun
 */
public class MyClassLoader extends ClassLoader{
 
    private File baseDir;
    
    MyClassLoader(){
        String basePath = MyClassLoader.class.getResource("").getPath();
        this.baseDir = new File(basePath);
    }
    
    @Override
    protected Class<?> findClass(String name) {
        String className = MyClassLoader.class.getPackage().getName() + "." + name;
        if(baseDir != null){
            File classFile = new File(baseDir,name.replaceAll("\\.", "/") + ".class");
            if(classFile.exists()){
                FileInputStream in = null;
                ByteArrayOutputStream out = null;
                try{
                    in = new FileInputStream(classFile);
                    out = new ByteArrayOutputStream();
                    byte [] buff = new byte[1024];
                    int len;
                    while ((len = in.read(buff)) != -1) {
                        out.write(buff, 0, len);
                    }
                    return defineClass(className, out.toByteArray(), 0,out.size());
                }catch (Exception e) {
                    e.printStackTrace();
                }finally{
                    if(null != in){
                        try {
                            in.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if(null != out){
                        try {
                            out.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    //先不删除,可以看到class文件内容
                    //classFile.delete();
                }
            }
        }
        return null;
    }
}

这里同样是实现【设计模式】——代理模式(动态)中媒婆的例子,其中Person接口以及Girl类在此不再赘述,MeiPo类因为Proxy以及MyInvocation的变化略微改动:

MyMeiPo.java

/** 媒婆 代理类
 * @author WangZhiJun
 */
public class MyMeiPo implements MyInvocationHandler {
 
    private Person target;
 
    /**获取被代理人的个人资料
     * @param target 被代理对象
     * @return Object
     */
    Object getInstance(Person target) {
        this.target = target;
        Class clazz = target.getClass();
        return MyProxy.newProxyInstance(new MyClassLoader(), clazz.getInterfaces(), this);
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是媒婆,按照你的要求");
        System.out.println("开始进行海选...");
        System.out.println("------------");
        //调用的时候
        method.invoke(this.target, args);
        System.out.println("------------");
        System.out.println("选择结束,如果合适的话,就准备办事");
        return null;
    }
}

客户端:

/**
 * @description: 客户端
 * @author: WangZhiJun
 * @create: 2019-08-22 13:51
 **/
public class TestCustomJDKProxy {
    public static void main(String[] args) {
        Person person= (Person)new MyMeiPo().getInstance(new Girl());
        person.findLove();
    }
}

运行结果:
20190822155709892.png

此时的结果与上节中我们利用jdk的Proxy和InvocationHanlder是一模一样的!

我们看看这个动态生成的$Proxy0代理类是怎么写的:

$Proxy0

public class $Proxy0 implements Person {
    MyInvocationHandler h;
 
    public $Proxy0(MyInvocationHandler var1) {
        this.h = var1;
    }
 
    public void findLove() {
        try {
            Method var1 = Person.class.getMethod("findLove");
            this.h.invoke(this, var1, (Object[])null);
        } catch (Throwable var2) {
            var2.printStackTrace();
        }
 
    }
}

这就是生成的动态代理类,同样是Person的实现类,同样也实现了findLove方法。

现在可以得知MyProxy.newProxyInstance返回的是动态生成的代理类$Proxy0的对象,也可以称作是Person接口的一个实现类的对象。当调用person.findLove()时,其实是调用$Proxy0.findLove(),然后对照刚刚的类调用关系图,即可调用到被代理对象Girl实例的findLove方法,从而实现了动态代理。

此时我们就简单的实现了JDK的动态代理,不过JDK中实际的实现会更加复杂,更加细节!


版权声明:文章转载请注明来源,如有侵权请联系博主删除!
最后修改:2019 年 12 月 26 日 05 : 16 PM
如果觉得我的文章对你有用,请随意赞赏
评论打卡也可以哦,您的鼓励是我最大的动力!