• 【Java入门提高篇】Day11 Java代理——JDK动态代理


      今天来看看Java的另一种代理方式——JDK动态代理

      我们之前所介绍的代理方式叫静态代理,也就是静态的生成代理对象,而动态代理则是在运行时创建代理对象。动态代理有更强大的拦截请求功能,因为可以获得类的运行时信息,可以根据运行时信息来获得更为强大的执(骚)行(操)力(作)。

      我们还是以上一个例子为例,这里的IStars接口和Stars类都不需要修改,只需要修改代理类。

      创建JDK动态代理需要先实现InvocationHandler接口,并重写其中的invoke方法,具体步骤如下:

      1. 创建一个类实现InvocationHandler接口。

      2. 给Proxy类提供委托类的ClassLoader和Interfaces来创建动态代理类。

      3. 利用反射机制得到动态代理类的构造函数。

      4. 利用动态代理类的构造函数创建动态代理类对象。

      我们用动态代理来改造一下之前的类:

      接口和委托类不需要修改:

    public interface IStars {
        void sing();
        void dance();
    }

      

    public class Stars implements IStars{
        private String name;
    
        public Stars(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void sing(){
            System.out.println(getName() + " 唱了一首歌.");
        }
    
        public void dance(){
            System.out.println(getName() + " 跳了一支舞.");
        }
    }

      这是使用动态代理后的代理类:

    public class StarsNewProxy implements InvocationHandler {
    
        //代理类持有委托类的对象引用
        private Object object;
    
        //保存sing和dance的次数
        private int num;
    
        public StarsNewProxy(Object object){
            this.object = object;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (!runBefore(method)){
                return null;
            };
            //利用反射机制将请求分派给委托类处理,Method的invoke返回Object对象作为方法执行结果
            Object result = method.invoke(object,args);
            runAfter(method);
            return result;
        }
    
        private boolean runBefore(Method method){
            System.out.println("我是代理,拦截到请求");
            if (method.getName().equals("dance")){
                System.out.println("抱歉,明星脚受伤了,不能跳舞表演了。");
                return false;
            }
            return true;
        }
    
        private void runAfter(Method method){
            System.out.println("我是代理,请求处理完毕");
        }
    }

      新建一个工厂类来返回代理实例:

    public class StarsNewProxyFactory {
        //构建工厂类,客户类调用此方法获得代理对象
        //对于客户类而言,代理类对象和委托类对象是一样的,不需要知道具体返回的类型
        public static IStars getInstance(String name){
            IStars stars = new Stars(name);
            InvocationHandler handler = new StarsNewProxy(stars);
            IStars proxy = null;
            proxy = (IStars) Proxy.newProxyInstance(
                    stars.getClass().getClassLoader(),
                    stars.getClass().getInterfaces(),
                    handler
            );
            return proxy;
        }
    }

      改写一下测试类:

    public class Test {
        public static void main(String[] args){
    //        testA();
            testB();
        }
    
        /**
         * 静态代理
         */
        private static void testA(){
            //创建目标对象
            IStars stars = new Stars("Frank");
    
            //代理对象,把目标传给代理对象,建立关系
            IStars starsProxy = new StarsProxy(stars);
            for (int i = 0;i < 5; i++){
                starsProxy.sing();
            }
        }
    
        /**
         * JDK动态代理
         */
        private static void testB(){
            IStars proxy = StarsNewProxyFactory.getInstance("Frank");
            proxy.dance();
            proxy.sing();
        }
    }

      输出如下:

    我是代理,拦截到请求
    抱歉,明星脚受伤了,不能跳舞表演了。
    我是代理,拦截到请求
    Frank 唱了一首歌.
    我是代理,请求处理完毕

      使用动态代理时实现了InvocationHandler接口并重写了invoke方法,invoke方法的三个参数:

    Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    
    proxy:  被代理的对象
    method:  被代理对象的某个方法的Method对象
    args:  被代理对象的某个方法接受的参数

      Proxy的newProxyInstance方法详情如下:

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
    
    loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
    
    interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
    
    h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

      可以看到,这里的动态代理跟静态代理一样,在代理类内部保存了一个委托类的实例,实际上都是调用原来的委托实例来进行需要的操作,代理类相当于给委托类加上一个外壳,把委托类置于代理类的内部,从而可以控制客户类对委托类的访问,就像上例中,代理类拦截了客户类对Stars类的dance方法的访问,并且输出了补充信息。

      动态代理跟静态代理最大的不同便是生成代理类的时期不同,静态代理是在编译期,而动态代理则是在运行时根据委托类信息动态生成。

      其次,动态代理实现的是InvocationHandler接口,而静态代理则是直接实现公共接口。当然动态代理也是需要实现相同的接口的,只是将接口信息放在了getInstance内部,相当于代理类跟委托类之间的约定,“这几个方法帮我代理一下吧”。

      最后,动态代理可以获得更多的运行时信息,使用起来也会更加灵活。

      至此,JDK动态代理讲解完毕,欢迎大家继续关注!

  • 相关阅读:
    js中常见事件
    第六周
    石家庄地铁售票系统
    第五周
    第四周
    html总结2
    返回一个整数数组中最大子数组的和(续)
    第九周总结
    团队冲刺第五天-KeepRunningAPP
    团队冲刺第四天-KeepRunningAPP
  • 原文地址:https://www.cnblogs.com/mfrank/p/8116789.html
Copyright © 2020-2023  润新知