一张图搞定JDK动态代理实现原理——手绘实现动态代理Demo

动态代理模式

说到动态代理模式,就一定要提到代理模式。代理模式类似于实际生活中的中介机构,可以给用户提供更好的服务,而不是直接与服务提供方交互。这种模式主要的目的有两个:
1.保护目标对象
2.增强目标对象

动态与静态代理模式

静态代理模式:源代码中需要声明代理类,单一的一对一的代理,可扩展性比较低。当需要代理的目标对象数量多的时候,这种模式的成本较高。
动态代理模式:源代码中无需声明代理类,它可以适应复杂多变的业务,适应性强,可以为一类目标对象提供代理服务,通用性好。

动态代理模式的实现方式

JDK原生实现方式

CGLib实现方式

JDK原生手写实现

实现原理

一张图搞定JDK动态代理实现原理——手绘实现动态代理Demo_第1张图片

手写实现

创建源文件和返回代理对象类:MyProxy
一张图搞定JDK动态代理实现原理——手绘实现动态代理Demo_第2张图片
在该类中有产生动态代理的实例对象方法,该实例对象是通过generateSourceFile(interfaces);得到的源程序文件编译得到的。
具体实现如下:

//    产生动态代理实例对象
    public static Object newProxyInstance(MyClassLoader classLoader,
                                          Class<?> [] interfaces,
                                          MyInvocationHandler handler) {
        try {
//            动态产生源代码.java文件
            String src = generateSourceFile(interfaces);
/*            java文件输出到磁盘中*/
//            MyProxy.class.getResource("");查找具有给定名称的资源。
//            用于搜索与给定类相关联的资源的规则由该类的定义class loader实现。
//            此方法委托给该对象的类加载器。 如果此对象由引导类加载器加载,
//            则该方法将委托给ClassLoader.getSystemResource(java.lang.String)
            URL url = MyProxy.class.getResource("");
            String path = url.getPath();//获取这个 URL的路径部分。
//            创建一个新的文件实例,将给定的字符串转为抽象地址路径
            File file = new File(path + "$Proxy0.java");
            FileWriter fw = new FileWriter(file);
            fw.write(src);//将生成的代码写入到文件:$Proxy0.java中
            fw.flush();
            fw.close();

           /* 将生成的Java文件编译成.class文件*/
//            获得programming language compiler编译器对象
            JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = javaCompiler
                    .getStandardFileManager(null,null,null);
            Iterable iterable = manager.getJavaFileObjects(file);
//            Creates a future for a compilation task with the given
//            components and arguments.
            JavaCompiler.CompilationTask task = javaCompiler
                    .getTask(null,manager,null,null,null,iterable);
            task.call();//启动编译
            manager.close();

            /*将编译生成的.class文件加载到JVM中*/
//            在类路径中寻找到$Proxy0.class文件并返回对象
            Class<?> proxyClass = classLoader.findClass("$Proxy0");
            assert proxyClass != null;
            Constructor<?> constructor = proxyClass.getConstructor(MyInvocationHandler.class);
            file.delete();//删除.java文件
//            返回字节码重组后的新的代理对象
            return constructor.newInstance(handler);//反射创建新的代理对象
        } catch (IOException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

而对于源程序的生成则是需要手动的根据需求编写产生程序。
先看被代理对象的类图:
一张图搞定JDK动态代理实现原理——手绘实现动态代理Demo_第3张图片
下面来看生成的.class文件:

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

package danran.proxy;

import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;

public class $Proxy0 implements School {
    MyInvocationHandler h;

    public $Proxy0(MyInvocationHandler var1) {
        this.h = var1;
    }

    public void Learn() {
        try {
            Method var1 = School.class.getMethod("Learn");
            this.h.invoke(this, var1, new Object[0]);
        } catch (Error var2) {
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }

    }
}

里面的关键代码:

		Method var1 = School.class.getMethod("Learn");
      	this.h.invoke(this, var1, new Object[0]);

说明它是通过反射机制去获得被代理对象的"Learn()"方法,然后通过去调用成员变量MyInvocationHandler h;"Learn()"方法,进而完成代理的功能。
MyInvocationHandler类:

public interface MyInvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

它的实现类:MyTestProxy

public class MyTestProxy implements MyInvocationHandler {
    private Object target;//被代理的对象的引用
//    返回代理对象实例
    public Object getInstance(Object target) throws Exception {
        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 {
        beforeAciton();
        Object obj = method.invoke(this.target,args);
        afterAction();
        return obj;
    }

    private void afterAction() {
       System.out.println("执行after....");
    }

    private void beforeAciton() {
     	System.out.println("执行before.....");
    }
}

上面去调用成员变量MyInvocationHandler h;"Learn()"方法时,会到这个实现类的invoke()方法中,执行增强的afterAction()beforeAciton()方法,以及反射调用实际的被代理的对象的"Learn()"方法,至此动态模式完成。
测试代码:

/**
 * @Classname MainTest
 * @Description TODO
 * @Date 2020/4/18 8:48
 * @Created by Jason
 */
public class MainTest {
    public static void main(String[] args) {
        try {
            School s = (School) new MyTestProxy().getInstance(new SchoolImpl());
            s.Learn();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

测试结果:
一张图搞定JDK动态代理实现原理——手绘实现动态代理Demo_第4张图片

你可能感兴趣的:(java学习)