美文网首页
Java 动态代理

Java 动态代理

作者: 程序员汪汪 | 来源:发表于2021-04-21 07:50 被阅读0次

    代理是基本的设计模式之一。在我们的日常生活中也很常见,比如:黄牛。

    代理模式设计的原理

    使用一个代理将原始对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

    静态代理

    假设xxx要开演唱会了,我(Me)想去看演唱会但是一票难求啊,怎么办?于是找到一个黄牛(Scalpers)。这个时候,我(Me)是被代理类,黄牛(Scalpers)是代理类。

    代码示例:

    interface Person {
        void pay();
    }
    
    // 被代理类
    class Me implements Person {
    
        @Override
        public void pay() {
            System.out.println("付钱......");
        }
    }
    
    // 代理类
    class Scalpers implements Person {
    
        private Person person;  // 这里需要用被代理对象进行实例化
    
        public Scalpers(Person factory) {
            this.person = factory;
        }
    
        @Override
        public void pay() {
            System.out.println("黄牛抢票成功......");
    
            person.pay();
    
            System.out.println("黄牛给票......");
        }
    }
    
    
    public class StaticProxyTest {
    
        public static void main(String[] args) {
    
            // 创建被代理类对象
            Me me = new Me();
            
            // 创建代理类对象
            Scalpers scalpers = new Scalpers(me); 
    
            scalpers.pay();
        }
    }
    

    静态代理的缺点:

    1. 代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。
    2. 每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。

    动态代理

    什么是动态代理?

    动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

    相比于静态代理的优点:

    抽象角色(接口)中声明的所有方法都被转移到一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。

    JDK动态代理的实现

    需要解决的问题:

    问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。

    (通过 Proxy.newProxyInstance() 实现)

    问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法。

    (通过 InvocationHandler 接口的实现类及其方法 invoke() )

    相关API

    Proxy类:专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。提供用于创建动态代理类和动态代理对象的静态方法。

    • static Class<?> getProxyClass(ClassLoader loader, Class<?>...interface) 创建一个动态代理类所对应的Class对象
    • static Object newProxyInstance(ClassLoader loader, Class<?>...interface, InvocationHandler h) 直接创建一个动态代理对象

    动态代理实现步骤

    1. 创建接口以及被代理类
    2. 创建一个实现接口 InvocationHandler 的类,它必须实现invoke()方法,以完成代理的具体操作。
    3. 通过Proxy的静态方法 newProxyInstance(ClassLoader loader, Class<?>...interface, InvocationHandler h) 创建一个接口代理
    4. 通过代理类的实例调用被代理类的方法

    代码示例:

    // 创建接口
    interface Person {
        void pay(int price); // 付钱
        String getId(); // 身份证号
    }
    
    // 被代理类
    class Me implements Person {
    
        @Override
        public void pay(int price) {
            System.out.println("付款:" + price + "元");
        }
    
        @Override
        public String getId() {
            return "123456789";
        }
    }
    /*
    要想实现动态代理,需要解决的问题?
    问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
    问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
     */
    
    // 创建继承了InvocationHandler接口的类
    class MyInvocationHandler implements InvocationHandler {
    
        private Object obj; // 需要使用被代理类的对象进行赋值
    
        public void bind (Object obj) {
            this.obj = obj;
        }
    
        //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
        //将被代理类要执行的方法a的功能就声明在invoke()中
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
            //obj:被代理类的对象
            Object returnValue = method.invoke(obj, args);
    
            //上述方法的返回值就作为当前类中的invoke()的返回值。
            return returnValue;
        }
    }
    
    class ProxyFactory {
        //调用此方法,返回一个代理类的对象。解决问题一
        public static Object getProxyInstance(Object obj) {
            MyInvocationHandler handler = new MyInvocationHandler();
            handler.bind(obj);
            return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
        }
    }
    
    //测试动态代理
    public class DynamicProxyTest {
    
        public static void main(String[] args) {
            Me me = new Me(); // 被代理对象
            //proxyInstance:代理类的对象
            Person proxyInstance = (Person) ProxyFactory.getProxyInstance(me);
            //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
            String id = proxyInstance.getId();
            System.out.println(id);
            proxyInstance.pay(100);
        }
    }
    

    相关文章

      网友评论

          本文标题:Java 动态代理

          本文链接:https://www.haomeiwen.com/subject/kgkalltx.html