静态代理和动态代理

一:概述

    Java的代理模式主要分为静态代理和动态代理,静态代理非常简单,简单说明一下,重点是动态代理。先来看下代理模式的类图(盗图):
代理模式.png

二:静态代理

    理解静态代理的经典场景就是买火车票,火车票代售点和火车站之间的关系就是静态代理的关系。代售点具有卖出火车票的功能,火车站也有卖票的功能,但是代售点卖票是依赖于火车站实现的,而火车站自己就实现了卖票功能;既然代售点和火车站都有卖票功能,那么可以把这个功能提取出来,封装在一个接口里面,然后代售点和火车站都实现这个接口,那么他们都有了卖票的功能:

public interface Ticket {
    void saleTicket();
}

    下面分别实现火车站类和代售点类:

//火车站类
public class TrainStation implements Ticket {

    @Override
    public void saleTicket() {
        System.out.println("我是火车站,1、2、3,卖票");
    }
}
//代售点类
public class ProxyStation implements Ticket {

    Ticket sale = new TrainStation();
    
    @Override
    public void saleTicket() {
        System.out.println("我是代售点,开始卖票");
        sale.saleTicket();
    }
}

    从上面的代码可以看出,代售点类持有了一个火车站类的引用,代售点卖票其实就是调用了火车站类的卖票方法,实际的卖票动作是在火车票进行的;代售点类就是个大骗子,什么事都使唤火车站类去做。测试代码如下:

public static void main(String[] args) {
    Ticket t1 = new ProxyStation();
    t1.saleTicket();
}

    输出结果如下:

我是代售点,开始卖票
我是火车站,1、2、3,卖票

    通过上面的代码,我们可以归纳出代理模式的三大要素:公共接口(Ticket)、真实对象(TrainStation)和代理对象(ProxyStation)。代理对象是供外部调用的对象,真实对象是真正实现公共接口逻辑的对象,公共接口就是抽象出代理对象和真实对象的公共功能的地方,这都很好理解。

    也许有人会说,为什么不能直接创建真实对象直接调用方法,而非要弄出一个中间人角色的代理对象出来?要知道,代理对象虽然是狐假虎威,但是他对隐藏真实对象是很有用的,外部不能直接与真实对象接触,这对保护数据什么的有很大作用;而且,在代理类里面也可以对方法进行增强:比如在卖票之前,检查身份证什么的;这样真实对象就可以专注于自己业务(卖票)的实现了。

    静态代理就是这么简单,没什么好说的。下面通过例子来说明动态代理。在上面的例子中,现在要求代售点在卖票之前,先检查身份证的有效性,这样就需要添加一个校验身份证的功能(方法)。根据开闭原则,既然接口、TrainStation和ProxyStation已经写好了,稳定了,那么就不应该再去该他了,怎么办?当然是创建一个新的代理类,在新的代理类里面实现校验身份证的功能:

public class ProxyStation1 implements Ticket{
    Ticket sale = new TrainStation();
    
    @Override
    public void saleTicket() {
        System.out.println("我是代售点,开始卖票");
        
        //原来的逻辑不变,但是添加检查身份证的功能
        boolean pass = checkID();
        if(pass) {
            sale.saleTicket();
        }
    }
    
    //检查身份证
    public boolean checkID() {
        //假装检查通过
        return true;
    }
}

    测试代码就不写了。可以看到,这样写没什么大问题,仅仅是新增了一个类,增加了检查身份证的方法,同时重写了卖票的方法。

    现在为了了解卖票过程的更具体的信息,需要计时功能,就是记录买一张票要多长时间,为铁道部以后的服务政策提供数据支撑。还是根据开闭原则,原来的类不能动,只能再新增一个类:

public class ProxyStation1 implements Ticket{
    Ticket sale = new TrainStation();
    
    //开始卖票和结束卖票的时间戳
    long start;
    long end;
    
    @Override
    public void saleTicket() {
        //开始计时
        start = System.currentTimeMillis();
        
        System.out.println("我是代售点,开始卖票");
        
        //原来的逻辑不变,但是添加检查身份证的功能
        boolean pass = checkID();
        if(pass) {
            sale.saleTicket();
            
            //结束计时
            end = System.currentTimeMillis();
            
            //计算卖一张票需要花费多少时间
            long duration = end - start;
        }
    }
    
    //检查身份证
    public boolean checkID() {
        //假装检查通过
        return true;
    }
}

    为了增加一个功能,又要实现一个类;当然,上面的类可以通过基础实现部分代码的复用,这里仅仅是为了举例,没去注意做。随着功能的越来越多,你就会发现新增的类就会越来越多,这样做肯定是不合理的,况且维护起来也非常麻烦。

    所以静态代理就会有这样的弊端,静态代理需要我们事先把代理类预先写好,预先写好有预先写好的好处,但是一旦需要扩展的时候,就会使得类的数量彭总;而且有些功能,比如检查身份证的方法checkID,在别的场景也能用得上,比如办银行卡;如果在一个办卡的类里面也要实现检查身份证功能,怎么办?还能咋地,再写一次呗,又不能把卖票的类引进来调用,毕竟卖票的类有很多功能和把银行卡没半毛钱关系,引进来不合适;计时功能可能在别的场景上遇得到,比如去营业厅办理业务,为了提高服务质量,首先要调查每次服务的时间,这个还是也要引入计时功能(假装计时功能封装在一个方法里面,而不是上面的获取两个时间戳然后相减),怎么办?再写一个与运营商有关的代理类呗,然后SB的把计时功能再写一遍(或者Ctrl + c然后Ctrl + v);可是这样做累不累?所以静态代理可能会导致代码量的急剧碰撞,一些相同功能得不到复用。

三:动态代理

    这个时候,动态代理就派上用场了。还是以卖票为例 ,公共接口和真实对象不变,从增加身份证检查的功能开始,先定义一个实现了InvocationHandler接口的类CheckIDHandler:

public class CheckIDHandler implements InvocationHandler {
    //真实对象类,在本例中就是火车站类
    private Object target;
    
    //检查身份证
    public boolean checkID() {
        //假装检查通过
        System.out.println("身份证检查通过");
        return true;
    }
    
    //InvocationHandler接口唯一的方法
    //要实现方法增强,就在这个方法里面加自己的逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法增强  
        checkID();
        Object result = method.invoke(target, args);
        return result;
    }
}

    动态代理的实现套路是这样:1.创建一个实现了InvocationHandler接口的类;2.增加自己的逻辑,用于方法增强;3.重写invoke方法,在method.invoke方法的前面(或者后面)增加自己的逻辑,实现方法增强。

    测试代码:

public static void main(String[] args) {
    //创建一个刚才新建的类的对象
    CheckIDHandler handler = new CheckIDHandler();
        
    //真实类对象
    TrainStation ts = new TrainStation();
        
    //创建真实类对象的代理对象,遥想当年,我们都
    //是先把代理类写好,然后一个个创建他们的对象
    //的;这里系统帮我们创建好代理对象并返回给我们
    Ticket p1 = (Ticket) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), 
                ts.getClass().getInterfaces(), handler);
        
    //拿到代理对象后,当然可以直接调用真实对象的方法了
    p1.saleTicket();
}

    动态代理的实现套路是这样:1.创建一个增强类的对象;2.创建一个真实类的对象;3.调用Proxy的静态方法newProxyInstance,传入当前线程的上下文类加载器,也可以是真实对象的类加载器,还有真实对象的接口(就是那个公共接口),最后一个就是增强类对象;4.经过 前面3步,就拿到了代理对象,然后就可以调用真实对象里面的方法了。

    输出结果如下:

身份证检查通过
我是火车站,1、2、3,卖票

    可以看到,我们没有定义代理类,更没有手动创建代理类的对象,就这样,在卖票之前做到了检查身份证的功能,实现了方法增强。

    也许你会说,卧槽,上面明明拿到了真实对象的引用,在真实对象的前面调用checkID()方法,不就是实现了检查身份证的功能吗?好吧,那么检查身份证的方法放在哪个类里面?调用者的类里面?如果别的地方也要用到检查身份证的功能呢?再写一遍?也许你会说放在工具类里面,但是不是所有的类都可以放在工具类里面当做静态方法来调的。
    把检查身份证的方法放在CheckIDHandler里面是有好处的,比如如果营业厅在办理业务时,要增加检查身份证的步骤,怎么办?

//下面是伪代码

//检查身份证的类
CheckIDHandler handler = new CheckIDHandler();

//营业厅的真实类
BusinessHall bh = new BusinessHall();
        
//Business是营业厅的接口,b1是返回的代理类
Business b1 = (Ticket) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), 
        ts.getClass().getInterfaces(), handler);
//调用办理业务的接口
b1.doBusiness();

    可以看到,通过引入CheckIDHandler就可以在办理业务之前,强行把检查身份证的动作强行插入进去,这也就是所谓的面向切面编程(AOP),同时可以看到的是,检查身份证的逻辑得到了极大的复用,另外上例中的计时功能也能封装到一个类,然后在买票创景和营业厅办理业务场景中复用。

    通过上面的对比可以看到,静态代理在要进行方法增强的时候,需要创建新的类,而且方法增强的越多,新增的类也就越多,同时这些增强的功能还没法复用,别的模块用不了,耦合太高,也造成了大量的冗余代码;而动态代理,每增强一次,也会创建一个类,但是这个类只关注增强的逻辑,与别的类耦合度较低,同时此类的增强方法还能用于别的模块。

    我个人的理解是动态代理就是把要增强的功能封装到一个类里面,然后在需要他的地方强行插入进入,一来不破坏原有的代码;二来增强功能能够得到复用。

    再盗一张图:
动态代理思维导图.jpg

四:动态代理的原理

    从上面例子中可以看出,动态代理的核心在于Proxy的newProxyInstance方法里面,我们去一探究竟:

......
    //注释略,太长了。第一个参数是真实对象的类加载器;第二个
    //参数是真实对象实现的接口最后一个参数是我们实现的增强类
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
            throws IllegalArgumentException {
        //增强类不能非空(判空新思路)
        Objects.requireNonNull(h);

        //将真实对象实现的接口拷到新数组
        final Class<?>[] intfs = interfaces.clone();

        //安全检查,不管
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         *
         * 查找或者生成指定的代理类,动态生成哦
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         *
         * 使用指定的调用处理程序调用其构造函数。
         */
        try {
            //安全检查,pass
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            //获取代理类的构造器
            final Constructor<?> cons = cl.getConstructor(constructorParams);


            final InvocationHandler ih = h;

            //getModifiers是Class的方法,返回的是该类的修饰符,比如public
            //,final,static,abstract,interface等,拿到这个修饰符后
            //,调用Modifier的isPublic方法判断该类是否是public类
            if (!Modifier.isPublic(cl.getModifiers())) {
                //如果该类不是public类,那么就要放开对这个类的访问权限,不管
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }

            //拿到Class对象后,就可以创建相应的实例了
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException | InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }
......

    newProxyInstance的实现思路是:1.首先调用getProxyClass0查找或者生成指定的代理类;2.检查该类是否是public的,如果不是,放开权限;3.调用Class的newInstance方法创建实例。最重要的是getProxyClass0方法:

private static Class<?> getProxyClass0(ClassLoader loader,
                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        //调用proxyClassCache的get方法;proxyClassCache对象是WeakCache类型的,他
        //持有一个弱引用队列,每次查找就去队列里面找,如果没找到就ProxyClassFactory的apply
        //方法去创建
        return proxyClassCache.get(loader, interfaces);
    }

    下面直接看ProxyClassFactory的apply方法:

        //第一个参数是真实对象的类加载器;第二个参数是真实对象实现的接口
        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            //创建一个Map容器,用于装载真实对象实现的接口
            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);

            //遍历这些接口,创建这些接口的Class对象,主要用于校验这些接口的合法性
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    //使用指定的类加载器创建一个与给定的名字关联的类或者接口的Class对象
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }

                //如果创建失败,说明类加载器不对
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                            intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                //如果创建出来的Class不是不是接口类型的,那么死给你看
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                            interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                //如果interfaceClass已经存在于容器中,说明重复创建了,那么也死给你看
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                            "repeated interface: " + interfaceClass.getName());
                }
            }

            //代理类的包名
            String proxyPkg = null;     // package to define proxy class in

            //将该类设置成public final类型的类,网上有方法可以拿到这个代理类,从
            //他反编译的结果来看,确实是public final类型的类,有兴趣的自己研究下
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            //又遍历,计算生成的代理类的包名
            for (Class<?> intf : interfaces) {

                //获取该接口的Class对象的访问修饰符
                int flags = intf.getModifiers();

                //如果不是public类型的
                if (!Modifier.isPublic(flags)) {
                    //那么去掉上面的设置成public final类型的操作
                    accessFlags = Modifier.FINAL;

                    //返回该接口的名字(包括包名)
                    String name = intf.getName();

                    //获取全限定名的最后的.号
                    int n = name.lastIndexOf('.');

                    //生成的代理类的包名,包名就是接口的全限定名 - 接口类的名字,类似于"java.lang."
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));

                    //将生成的包名赋值给代理类的包名
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                                "non-public interfaces from different packages");
                    }
                }
            }

            //如果木有非公共代理接口,那么使用默认的包名com.sun.proxy
            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            //生成一个数字,过程不管
            long num = nextUniqueNumber.getAndIncrement();

            //拼接代理类的全限定名
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
            //生成代理类的byte数组,没看到源码,不管了
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
            try {
                //将byte数组转换成Class对象并返回
                return defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }

     apply方法比较长,但是逻辑并不复杂,不过出现了一些我们很少用到的接口,查看API就可以了,不过最后还是没看到生成byte数组的generateProxyClass方法,从网上摘抄了一部分资料:

public static byte[] generateProxyClass(final String name,  
                                           Class[] interfaces)  
   {  
       ProxyGenerator gen = new ProxyGenerator(name, interfaces);  
       // 这里动态生成代理类的字节码
       final byte[] classFile = gen.generateClassFile();  
 
       // 如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上  
       if (saveGeneratedFiles) {  
           java.security.AccessController.doPrivileged(  
           new java.security.PrivilegedAction<Void>() {  
               public Void run() {  
                   try {  
                       FileOutputStream file =  
                           new FileOutputStream(dotToSlash(name) + ".class");  
                       file.write(classFile);  
                       file.close();  
                       return null;  
                   } catch (IOException e) {  
                       throw new InternalError(  
                           "I/O exception saving generated file: " + e);  
                   }  
               }  
           });  
       }  
  
       // 返回代理类的字节码  
       return classFile;  
   }  

private byte[] generateClassFile() {
        //第一步, 将所有的方法组装成ProxyMethod对象
  3     //首先为代理类生成toString, hashCode, equals等代理方法
  4     addProxyMethod(hashCodeMethod, Object.class);
  5     addProxyMethod(equalsMethod, Object.class);
  6     addProxyMethod(toStringMethod, Object.class);
  7     //遍历每一个接口的每一个方法, 并且为其生成ProxyMethod对象
  8     for (int i = 0; i < interfaces.length; i++) {
  9         Method[] methods = interfaces[i].getMethods();
 10         for (int j = 0; j < methods.length; j++) {
 11             addProxyMethod(methods[j], interfaces[i]);
 12         }
 13     }
 14     //对于具有相同签名的代理方法, 检验方法的返回值是否兼容
 15     for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
 16         checkReturnTypes(sigmethods);
 17     }
 18     
 19     //第二步, 组装要生成的class文件的所有的字段信息和方法信息
 20     try {
 21         //添加构造器方法
 22         methods.add(generateConstructor());
 23         //遍历缓存中的代理方法
 24         for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
 25             for (ProxyMethod pm : sigmethods) {
 26                 //添加代理类的静态字段, 例如:private static Method m1;
 27                 fields.add(new FieldInfo(pm.methodFieldName,
 28                         "Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC));
 29                 //添加代理类的代理方法
 30                 methods.add(pm.generateMethod());
 31             }
 32         }
 33         //添加代理类的静态字段初始化方法
 34         methods.add(generateStaticInitializer());
 35     } catch (IOException e) {
 36         throw new InternalError("unexpected I/O Exception");
 37     }
 38     
 39     //验证方法和字段集合不能大于65535
 40     if (methods.size() > 65535) {
 41         throw new IllegalArgumentException("method limit exceeded");
 42     }
 43     if (fields.size() > 65535) {
 44         throw new IllegalArgumentException("field limit exceeded");
 45     }
 46 
 47     //第三步, 写入最终的class文件
 48     //验证常量池中存在代理类的全限定名
 49     cp.getClass(dotToSlash(className));
 50     //验证常量池中存在代理类父类的全限定名, 父类名为:"java/lang/reflect/Proxy"
 51     cp.getClass(superclassName);
 52     //验证常量池存在代理类接口的全限定名
 53     for (int i = 0; i < interfaces.length; i++) {
 54         cp.getClass(dotToSlash(interfaces[i].getName()));
 55     }
 56     //接下来要开始写入文件了,设置常量池只读
 57     cp.setReadOnly();
 58     
 59     ByteArrayOutputStream bout = new ByteArrayOutputStream();
 60     DataOutputStream dout = new DataOutputStream(bout);
 61     try {
 62         //1.写入魔数
 63         dout.writeInt(0xCAFEBABE);
 64         //2.写入次版本号
 65         dout.writeShort(CLASSFILE_MINOR_VERSION);
 66         //3.写入主版本号
 67         dout.writeShort(CLASSFILE_MAJOR_VERSION);
 68         //4.写入常量池
 69         cp.write(dout);
 70         //5.写入访问修饰符
 71         dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
 72         //6.写入类索引
 73         dout.writeShort(cp.getClass(dotToSlash(className)));
 74         //7.写入父类索引, 生成的代理类都继承自Proxy
 75         dout.writeShort(cp.getClass(superclassName));
 76         //8.写入接口计数值
 77         dout.writeShort(interfaces.length);
 78         //9.写入接口集合
 79         for (int i = 0; i < interfaces.length; i++) {
 80             dout.writeShort(cp.getClass(dotToSlash(interfaces[i].getName())));
 81         }
 82         //10.写入字段计数值
 83         dout.writeShort(fields.size());
 84         //11.写入字段集合 
 85         for (FieldInfo f : fields) {
 86             f.write(dout);
 87         }
 88         //12.写入方法计数值
 89         dout.writeShort(methods.size());
 90         //13.写入方法集合
 91         for (MethodInfo m : methods) {
 92             m.write(dout);
 93         }
 94         //14.写入属性计数值, 代理类class文件没有属性所以为0
 95         dout.writeShort(0);
 96     } catch (IOException e) {
 97         throw new InternalError("unexpected I/O Exception");
 98     }
 99     //转换成二进制数组输出
100     return bout.toByteArray();
101 }

    至此,我们基本上了解了代理对象是怎么生成的。
    还有个问题是,那个invoke是什么时候调用的呢?要想解开这个谜题,只能查看动态生成的代理对象的源码,同样从网上摘抄了一个代理对象的源码:

{
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;
  
  /**
  *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
  *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
  *被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
  *
  *super(paramInvocationHandler),是调用父类Proxy的构造方法。
  *父类持有:protected InvocationHandler h;
  *Proxy构造方法:
  *    protected Proxy(InvocationHandler h) {
  *         Objects.requireNonNull(h);
  *         this.h = h;
  *     }
  *
  */
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }
  
  //这个静态块本来是在最后的,我把它拿到前面来,方便描述
   static
  {
    try
    {
      //看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
 
  /**
  * 
  *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
  *this.h.invoke(this, m3, null);这里简单,明了。
  *来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
  *再联系到InvacationHandler中的invoke方法。嗯,就是这样。
  */
  public final void giveMoney()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

    //注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和    giveMoney方法一毛一样。
}

    上面代码的giveMoney就相当于上面例子中的checkID()。可以看到,当我们调用代理对象的checkID()方法时,代理对象内部调用了增强类的invoke方法,正式在这个invoke方法里面,我们才能实现方法的增强,当然在调用invoke的时候,我们还把最终要调用的买票的方法saleTicket传进去了,这个方法最终通过反射的机制得到调用。

推荐阅读更多精彩内容

  • 作者: 一字马胡 转载标志 【2017-11-03】 更新日志 Java静态代理 代理,即是将自己需要做的一些事...
    一字马胡阅读 1,441评论 0 13
  • 1、代理概念 为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用...
    孔垂云阅读 4,317评论 4 51
  • 一、代理概念 为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用...
    Fredia_Wang阅读 308评论 0 5
  • 一、概述 代理是一种模式,提供了对目标对象的间接访问方式。通常是用户可能出于某些原因不能直接访问目标对象,而此时通...
    hu1991die阅读 138评论 2 3
  • 身体是乐器,用前先擦拭。清理杂物去,位置要摆正。低音在上位,高音力在腰。声道打开后,咿呀吁呦声。呼吸占八十,二十在...
    草与泥阅读 25评论 0 0