从简入繁详解代理模式(手写JDK动态代理)

1 简单概要

代理模式:指为其他对象提供一种代理来控制对这个对象的访问。

 代理模式主要有两个目的:

①保护目标对象;

②增强目标对象。

它的类图如下:

从简入繁详解代理模式(手写JDK动态代理)_第1张图片

2 分类与详解

2.1 静态代理

举个栗子:儿子正在找对象,而父母希望儿女早点找到另一半,于是在儿子找对象的同时帮他物色:

interface Person{
    void findLove();
}

public class Son implements Person {
    public void findLove() {
        System.out.println("儿子要求:是个女的");
    }
}

父亲帮找:

public class Father {
    private Son son;

    public Father(Son son){
        this.son = son;
    }

    public void findLove(){
        System.out.println("父亲帮找");
        this.son.findLove();
    }
}

Client类:

public class Client {
    public static void main(String[] args) {
        Father father = new Father(new Son());
        father.findLove();
    }
}

运行:

从简入繁详解代理模式(手写JDK动态代理)_第2张图片

我们现在想让父亲帮表妹也找找,此时代码就需要修改Father类,不符合开闭原则,所以做如下优化:

public class Father {
    private Person person;

    public Father(Person person){
        this.person = person;
    }

    public void findLove(){
        System.out.println("父亲帮找");
        this.person.findLove();
    }
}

当Father中的Son替换为Person时,我们就可以代理所有实现了Person接口的类,表妹也是人,所以只需要增加一个表妹类,Client传参时传入new BiaoMei()就可以了,此时的代码对修改关闭,对扩展开放,符合开闭原则。

2.2 动态代理

如果父亲不仅要帮儿子找对象,也要帮很多人找对象,也就是媒婆,我们不可能创建上百个想表妹一样的类,所以有没有一种更加通用的解决方案呢?也许动态代理可以帮助我们。

2.2.1 JDK方式实现动态代理(手写JDK动态代理)

创建消费者类Customer(在媒婆处消费):

interface Person{
    void findLove();
}

public class Customer implements Person {
    public void findLove() {
        System.out.println("女的就行");
    }
}

创建媒婆类(Meipo):

public class Meipo implements InvocationHandler {
    //被代理的对象,保存起来方便invoke中的this.target的调用
    private Object target;

    public Object getInstance(Object target){
        this.target = target;
        Class aClass = target.getClass();
        
        //创建代理类并返回
        return Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        befor();
        Object invoke = method.invoke(this.target, args);
        after();
        return invoke;
    }

    private void befor(){
        System.out.println("媒婆帮找");
    }

    private void after(){}
}

创建Client类:

public class Client {
    public static void main(String[] args) {
        Person person = (Person) new Meipo().getInstance(new Customer());
        person.findLove();
    }
}

运行:

从简入繁详解代理模式(手写JDK动态代理)_第3张图片

那么动态代理是如何实现的呢?

她其实就是通过字节重组,重新生成对象来代替原始对象,以达到代理的目的。

字节码重组的基本步骤如下:

①获取被代理对象的引用,利用反射获取到它的所有接口;

②JDK动态代理类Proxy重新生成一个新的类,此类要实现刚才获取到的所有接口;

③动态生成新类的Java代码;

④编译.java文件成.class文件;

⑤加载编译好的.class文件。

 那么修改Client类,去查看一下生成好的.class文件:

public class Test {
    public static void main(String[] args) {
        try {
            Person person = (Person) new Meipo().getInstance(new Customer());
            person.findLove();
            byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
            FileOutputStream fos = new FileOutputStream(new File("$Proxy0.class"));
            fos.write(bytes);
            fos.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

运行:

从简入繁详解代理模式(手写JDK动态代理)_第4张图片

查看$Proxy0.class($开头的类是动态生成的):

public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

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

    ......

    public final void findLove() throws  {
        try {
            super.h.invoke(this, m3, (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"));
            m3 = Class.forName("cn.xupt.design_pattern.structure.proxy.动态代理.JDK.Person").getMethod("findLove");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

此类实现了Person接口、继承了Proxy类,并在findlove()中调用了父类Proxy的h.invoke(this, m3, (Object[])null),这个h的类型就是InvocationHandler。之后我们就是使用的这个类的对象去调用findLove()方法的。

了解JDK的原理后来手写它的实现

创建Person接口:

public interface Person {
    void findLove();
}

创建Customer类:

public class Customer implements Person{
    public void findLove() {
        System.out.println("女的就行");
    }
}

创建MyInvocationHandler接口:

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

自定义类加载器MyClassLoader:

public class MyClassLoader extends ClassLoader {
    private File classPathFile;

    public MyClassLoader(){
        //class文件路径
        String classpath = MyClassLoader.class.getResource("").getPath();
        this.classPathFile = new File(classpath);
    }

    /**
     * 寻找需要加载的class文件并返回类对象
     * @param name :类名称
     * @return :找到的Class对象
     * @throws ClassNotFoundException
     */
    protected Class findClass(String name) throws ClassNotFoundException {
        String className = MyClassLoader.class.getPackage().getName() + "." + name;
        if(null != classPathFile){
            File classFile = new File(classPathFile, name + ".class");
            if(classFile.exists()){
                FileInputStream fis = null;
                ByteArrayOutputStream baos = null;

                try{
                    fis = new FileInputStream(classFile);
                    baos = new ByteArrayOutputStream();
                    byte[] buff = new byte[1024];
                    int len;
                    while((len = fis.read(buff)) != -1){
                        baos.write(buff, 0, len);
                    }
                    return defineClass(className, baos.toByteArray(), 0, baos.size());
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    if(null != fis){
                        try {
                            if (null != fis) {
                                fis.close();
                            }
                            if (null != baos) {
                                baos.close();
                            }
                        }catch (IOException e){
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return null;
    }
}

自定义Proxy类MyProxy:

package cn.xupt.design_pattern.structure.proxy.handwriting_dynamic_proxy;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class MyProxy {
    public static final String ln = "\r\n";

    public static Object newMyProxyInstance(MyClassLoader classLoader, Class[] interfaces, MyInvocationHandler handler){
        try {
            String src = generateSrc(interfaces);

            //将Java文件输出到磁盘上
            String filePath = MyProxy.class.getResource("").getPath() + "$Proxy0.java";
            File file = new File(filePath);
            FileWriter fw = new FileWriter(file);
            fw.write(src);
            fw.flush();
            fw.close();

            //编译Java文件成class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
            Iterable iterator = manager.getJavaFileObjects(file);

            JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterator);
            task.call();
            manager.close();

            //加载类
            Class proxyClass = classLoader.findClass("$Proxy0");
            Constructor constructor = proxyClass.getConstructor(MyInvocationHandler.class);
            file.delete();

            return constructor.newInstance(handler);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 构造如下类
     *  package cn.xupt.design_pattern.structure.proxy.handwriting_dynamic_proxy;
     *  import cn.xupt.design_pattern.structure.proxy.handwriting_dynamic_proxy.Person;
     *  import java.long.reflect.*;
     *
     *  public class $Proxy0 implements Person;
     *      MyInvocationHandler handler;
     *      public $Proxy0(MyInvocationHandler handler) {
     *          this.handler = handler;
     *      }
     *
     *      public int createOrder(Order order){
     *          try{
     *              Method method = interfaces[0].getMethod("createOrder", new Class[]{Order.class});
     *              return ((Integer) this.handler.invoke(this, m, new Object[]{order})).intValue();
     *          }catch(Error _ex){}
     *          catch(Throwable e){
     *              throw new UndeclaredThrowableException(e);
     *          }
     *          return 0;
     *      }
 *      }
     *
     * @param interfaces
     * @return String 返回创建好的Java源代码
     */
    private static String generateSrc(Class[] interfaces){
        StringBuffer sb = new StringBuffer();
        sb.append("package cn.xupt.design_pattern.structure.proxy.handwriting_dynamic_proxy;" + ln);
        sb.append("import cn.xupt.design_pattern.structure.proxy.handwriting_dynamic_proxy.Person;" + ln);
        sb.append("import java.lang.reflect.*;" + ln);
        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
            sb.append("MyInvocationHandler handler;" + ln);
            sb.append("public $Proxy0(MyInvocationHandler handler) {" + ln);
                sb.append("this.handler = handler;");
            sb.append("}" + ln);
            for(Method m:interfaces[0].getMethods()){
                //获得每个方法的形式参数
                Class[] params = m.getParameterTypes();

                StringBuffer paramNames = new StringBuffer();
                StringBuffer paramValus = new StringBuffer();
                StringBuffer paramClasses = new StringBuffer();

                for (int i = 0; i < params.length; i++) {
                    Class clazz = params[i];
                    String type = clazz.getName();
                    //将类写名称第一个字母小写 order
                    String paramName = toLowerFirstCase(clazz.getSimpleName());
                    //cn.xupt.design_pattern.structure.proxy.handwriting_dynamic_proxy.Order order
                    paramNames.append(type + " " + paramName);
                    paramValus.append(paramName);
                    //cn.xupt.design_pattern.structure.proxy.handwriting_dynamic_proxy.Order.class
                    paramClasses.append(clazz.getName() + ".class");

                    if (i > 0 && i < params.length - 1) {
                        paramNames.append(",");
                        paramClasses.append(",");
                        paramValus.append(",");
                    }
                }

                //构造各个方法
                sb.append("public " + m.getReturnType().getName() + " " + m.getName() + " " + "(" +
                        paramNames.toString() + "){" + ln);
                    sb.append("try{" + ln);
                        sb.append("Method method = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName()
                                + "\", new Class[]{" + paramClasses.toString() + "});" + ln);
                        sb.append((hasReturnValue(m.getReturnType()) ? "return " : "") + getCaseCode(
                                "this.handler.invoke(this, method, new Object[]{" + paramValus + "})", m.getReturnType()) + ";");
                    sb.append("}catch(Error _ex){}");
                    sb.append("catch(Throwable e){" + ln);
                    sb.append("throw new UndeclaredThrowableException(e);" + ln);
                    sb.append("}");
                    sb.append(getReturnEmptyCode(m.getReturnType()));
                sb.append("}");
            }
            sb.append("}" + ln);
            return sb.toString();
    }

    private static Map mappings = new HashMap();
    static {
        mappings.put(int.class, Integer.class);
    }

    private static String getReturnEmptyCode(Class returnClass){
        if(mappings.containsKey(returnClass)){
            return "return 0";
        }
        else if(returnClass == void.class){
            return "";
        }
        else {
            return "return null";
        }
    }

    private static String getCaseCode(String code, Class returnClass){
        if(mappings.containsKey(returnClass)) {
            return "((" + mappings.get(returnClass).getName() + ")" + code + ")." + returnClass.getSimpleName() + "Value()";
        }
        return code;
    }

    /**
     * @param clazz
     * @return true 不是void类型
     * @return false 是vodid类型
     */
    private static boolean hasReturnValue(Class clazz){
        return clazz != void.class;
    }

    private static String toLowerFirstCase(String src){
        char[] chars = src.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }
}

创建媒婆类MyMeipo:

public class MyMeipo implements MyInvocationHandler {
    private Object target;

    public Object getInstance(Object target){
        this.target = target;
        Class aClass = target.getClass();
        return MyProxy.newMyProxyInstance(new MyClassLoader(), aClass.getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        befor();
        Object invoke = method.invoke(this.target, args);
        after();
        return invoke;
    }

    private void befor(){
        System.out.println("我是媒婆");
    }

    private void after(){
        System.out.println("找到了!");
    }
}

创建Client类:

public class Test {
    public static void main(String[] args) {
        Person person = (Person) new MyMeipo().getInstance(new Customer());
        person.findLove();
    }
}

运行:

从简入繁详解代理模式(手写JDK动态代理)_第5张图片

有一些遇到的问题我放在下面,以免老哥们踩坑:

1. 遇到FileNotFoundException?

解决:路径不能带中文,带中文会出错。

2. 遇到NoClassDefFoundError?

解决:jar包没有导入tools.jar,如何导入请看警告: Can't initialize javac processor due to (most likely) a class loader problem: java.lang.NoClassD_Hello_xzy_Word的博客-CSDN博客

3. 遇到Exception in thread "main" java.lang.IllegalAccessError: class cn.xupt.design_pattern.structure.proxy.handwriting_dynamic_proxy.$Proxy0 cannot access its superinterface cn.xupt.design_pattern.structure.proxy.handwriting_dynamic_proxy.Person?

解决:interface Person应加上public(单独写一个接口)。

2.2.2 Cglib实现动态代理

还是以找对象为栗子:

创建CgMeipo类:

public class CgMeipo implements MethodInterceptor {

    public Object getInstance(Class clazz){
        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 {
        before();
        Object obj = methodProxy.invokeSuper(o, objects);
        after();
        return obj;
    }

    private void before(){
        System.out.println("我是媒婆");
    }

    private void after(){

    }
}

创建Customer类(由于Cglib是通过动态继承被代理者来实现动态代理的,所以不需要实现Person接口):

public class Customer {
    public void findLove(){
        System.out.println("女的就行");
    }
}

创建Client类:

public class Test {
    public static void main(String[] args) {
        try {
            Customer customer = (Customer) new CgMeipo().getInstance(Customer.class);
            customer.findLove();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

运行:

从简入繁详解代理模式(手写JDK动态代理)_第6张图片

我们可以在Client类中加入:

public class Test {
    public static void main(String[] args) {
        try {
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./cglib_classes/");
            Customer customer = (Customer) new CgMeipo().getInstance(Customer.class);
            customer.findLove();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

将生成的.class文件输出到磁盘中,运行后:

从简入繁详解代理模式(手写JDK动态代理)_第7张图片

发现有一个类继承了Customer,那说明这个类就是我们生成的代理类了:

public class Customer$$EnhancerByCGLIB$$b0a90fbe extends Customer implements Factory {

    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$findLove$0$Method;
    private static final MethodProxy CGLIB$findLove$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$finalize$1$Method;
    private static final MethodProxy CGLIB$finalize$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("cn.xupt.design_pattern.structure.proxy.cglib_proxy.Customer$$EnhancerByCGLIB$$b0a90fbe");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = var10000[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
        CGLIB$equals$2$Method = var10000[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        CGLIB$toString$3$Method = var10000[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        CGLIB$hashCode$4$Method = var10000[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = var10000[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        CGLIB$findLove$0$Method = ReflectUtils.findMethods(new String[]{"findLove", "()V"}, (var1 = Class.forName("cn.xupt.design_pattern.structure.proxy.cglib_proxy.Customer")).getDeclaredMethods())[0];
        CGLIB$findLove$0$Proxy = MethodProxy.create(var1, var0, "()V", "findLove", "CGLIB$findLove$0");
    }

    final void CGLIB$findLove$0() {
        super.findLove();
    }

    public final void findLove() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$findLove$0$Method, CGLIB$emptyArgs, CGLIB$findLove$0$Proxy);
        } else {
            super.findLove();
        }
    }

    ......

可以发现,此类重写了父类Customer的所有方法,并且每一个方法都有MethodProxy与之对应,如findLove()的CGLIB$clone$5$Proxy,这些在findLove()方法中都有调用,如:

var10000.intercept(this, CGLIB$findLove$0$Method, CGLIB$emptyArgs, CGLIB$findLove$0$Proxy);

此时可以分析出cglib的调用流程了:

代理对象调用this.findLove() ---> 调用拦截器(此时执行before和after) ---> methodProxy.invokeSuper()方法 ---> CGLIB$findLove$0 ---> 被代理者的findLove()方法

那methodProxy是如何通过invokeSuper来获取代理方法的呢?点开invokeSuper():

    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }

    ......

    private static class FastClassInfo {
        FastClass f1;
        FastClass f2;
        int i1;
        int i2;

        private FastClassInfo() {
        }
    }

我们发现invokeSuper()调用了fci.f2.invoke(fci.f2, obj, args),点开invoke进入到FastClass类:

public abstract class FastClass {

    ......

    public abstract Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException;

是一个抽象类,还记得前面生成的三个.class文件吗?它们其实就是分别为代理类和被代理类生成的FastClass。

点开一个:

public class Customer$$EnhancerByCGLIB$$b0a90fbe$$FastClassByCGLIB$$6a538f19 extends FastClass {

    ......

发现它继承了FastClass,并实现了invoke:

public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        b0a90fbe var10000 = (b0a90fbe)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                return new Boolean(var10000.equals(var3[0]));
            case 1:
                return var10000.toString();
            case 2:
                return new Integer(var10000.hashCode());
            case 3:
                return var10000.newInstance((Callback)var3[0]);
            case 4:
                return var10000.newInstance((Callback[])var3[0]);
            case 5:
                return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
            case 6:
                var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
                return null;
            case 7:
                var10000.findLove();
                return null;
            case 8:
        
        ......

从中看出方法的区分是通过第一个参数var1来进行的,这个参数其实就是FastClass机制生成的,它为代理类和被代理类各生成一个类,这个类会为代理类或被代理类的方法分配一个index(int),这个index当作一个入参,FastClass就可以直接定位方法,而不需要使用反射来获取,所以效率会更高。这个index生成在getIndex()中(getIndex在生成的两个类中,FastClass中的是抽象方法):

    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case -2055565910:
            if (var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
                return 11;
            }
            break;
        case -1725733088:
            if (var10000.equals("getClass()Ljava/lang/Class;")) {
                return 24;
            }
            break;
        case -1457535688:
            if (var10000.equals("CGLIB$STATICHOOK1()V")) {
                return 20;
            }
            break;
        ......

FastClass并不是和代理类一起生成的,而是在第一次执行invokeSuper时生成并放入缓存内。

工作感悟:

2023/3/8:今天在修改一部分功能时,修改了持久化的一部分内容,是关于Exception的,这个Exception具体使用地方是使用了它的toString,但是我想让它使用getMessage代替toString,因为toString会打印出来类路径,我不想又类路径,但是如果我直接修改它的使用地方,因为Exception是一个很抽象的概念,使用它的地方也是抽象的,修改后肯定会有很大影响。所以我在我的持久化内容这里使用了动态代理,让只有我持久化生成的Exception代理类使用toString时改成getMessage。

其实上面的使用时有问题的,因为jdk的动态代理只能代理接口,而cglib又依赖不到,所以最后是用了ExceptionWrapperToString继承了Exception,重写toString,这样写了发现我的场景也没问题,所以也不需要用代理,以上是由工作联想的思考,故记录下来。

感谢观看0.0

你可能感兴趣的:(设计模式,代理模式,java,开发语言)