Android进阶

Android-动态代理全面剖析

2021-01-12  本文已影响0人  沉淀者

一、概念

动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。即不直接找到被代理对象,而是找被代理对象的经纪人,从而由经纪人在找到被代理对象。简单来说代理是一种软件设计模式,某些情况下,我们不希望修改已有对象的功能,但是我们又想在不改变调用者调用方法的情况下,改变调用后的具体实现逻辑,因此,我们会采用间接访问来实现目的。比如A类实现了IHelloWorld接口,有一个helloWorld方法,但在使用的时候并不直接调用a.helloWorld(),而是通过定一个同样实现了IHelloWorld接口的代理类B,在B的helloWorld方法中调用A的helloWorld方法,并夹带一些自己的私货。这样不但不会改动原有代码又可以在原有的方法里面增添自己需要的代码。

二、应用场景

1.基于静态代理应用场景下,需要代理对象数量较多的情况下使用动态代理
2.AOP 领域
定义:即 Aspect Oriented Programming = 面向切面编程,是OOP的延续、函数式编程的一种衍 生范型
作用:通过预编译方式和运行期动态代理实现程序功能的统一维护。
优点:降低业务逻辑各部分之间的耦合度 、 提高程序的可重用性 & 提高了开发的效率
具体应用场景:日志记录、性能统计、安全控制、异常处理等

三、具体应用

背景:小成 希望买一台最新的顶配 Mac 电脑;小何希望买一台 iPhone
冲突:国内还没上,只有美国才有
解决方案:寻找一个代购一起进行购买
即1个代购(动态代理对象)同时 代替 小成 & 小何(目标对象) 去买Mac(间接访问的操作)
该代购是代购任何商品 = 什么人有什么需求就会去代购任何东西(动态代理)

四、使用步骤

1.声明 调用处理器类
2.声明目标对象类的抽象接口
3.声明目标对象类
4.通过动态代理对象,调用目标对象的方法

五、具体代码

1.声明目标对象的抽象接口,也就是具体要做的事情,通过接口定义需要具体完成的业务,也就是 代理对象
Subject.java

public interface Subject {
    // 定义目标对象的接口方法
    // 代购物品
    public  void buybuybuy();

}

2.声明目标对象类,也就是真正的对象,也叫作被代理对象
Buyer1.java

// 小成,真正的想买Mac的对象 = 目标对象 = 被代理的对象
// 实现抽象目标对象的接口
public class Buyer1 implements Subject  {

    @Override
    public void buybuybuy() {
        System.out.println("小成要买Mac");
    }

}

Buyer2.java

// 小何,真正的想买iPhone的对象 = 目标对象 = 被代理的对象
// 实现抽象目标对象的接口
public class Buyer2 implements Subject  {

    @Override
    public void buybuybuy() {
        System.out.println("小何要买iPhone");
    }

}

3. 声明调用处理器类
DynamicProxy.java

public class DynamicProxy implements InvocationHandler {

        //目标对象,也就是被代理对象,就是上文的Buyer1 和Buyer12
        private Object realObject;

        public Object newProxyInstance(Object realObject){
            this.realObject =realObject;
            //返回的是代理对象,这里就是Subject这个接口
            return Proxy.newProxyInstance(realObject.getClass().getClassLoader(),
                    realObject.getClass().getInterfaces(),this);

            // Proxy.newProxyInstance()作用:根据指定的类装载器、一组接口 & 调用处理器 生成动态代理类实例,并最终返回
            // 参数说明:
            // 参数1:指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
            // 参数2:指定目标对象的实现接口
            // 即要给目标对象提供一组什么接口。若提供了一组接口给它,那么该代理对象就默认实现了该接口,这样就能调用这组接口中的方法
            // 参数3:指定InvocationHandler对象。即动态代理对象在调用方法时,会关联到哪个InvocationHandler对象

        }


        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
        //可以在这里增加一些自己需要的逻辑

        // 参数说明:
        // 参数1:也就是当前的代理对象Object(不是被代理对象)
        // 参数2:当前执行的方法
        // 参数3:当前执行方法的参数
                throws Throwable {
            System.out.println("代购出门了");
            Object result;
            // 通过Java反射机制调用目标对象方法,也就是通过这里调用到了目标对象
            /*举例,相当于执行了被代理对象的方法
            Buyer1 mBuyer1 = new Buyer1();
            mBuyer1.buybuybuy();
            */
            result = method.invoke(realObject, args);
            return result;
        }
    }

4.通过调用代理对象,从而调用目标对象
MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 1. 创建调用处理器类对象
        DynamicProxy DynamicProxy = new DynamicProxy();

        // 2. 创建目标对象对象
        Buyer1 mBuyer1 = new Buyer1();

        // 3. 创建动态代理类 & 对象:通过调用处理器类对象newProxyInstance()
        // 传入上述目标对象对象
        Subject Buyer1_DynamicProxy = (Subject) DynamicProxy.newProxyInstance(mBuyer1);

        // 4. 通过调用动态代理对象方法从而调用目标对象方法
        // 实际上是调用了invoke(),再通过invoke()里的反射机制调用目标对象的方法
        Buyer1_DynamicProxy.buybuybuy();
        // 以上代购为小成代购Mac

        // 以下是代购为小何代购iPhone
        Buyer2 mBuyer2 = new Buyer2();
        Subject Buyer2_DynamicProxy = (Subject) DynamicProxy.newProxyInstance(mBuyer2);
        Buyer2_DynamicProxy.buybuybuy();
    }
}

六、源码解析

怎么通过调用动态代理对象方法从而调用目标对象方法?

DynamicProxy.newProxyInstance()生成了一个动态代理类 及其实例
该动态代理类记为 :$Proxy0
下面我们直接看该类实现及其 buybuybuy()
该方法的逻辑如下:

<-- 动态代理类 $Proxy0 实现-->
// 继承:Java 动态代理机制的主类:java.lang.reflect.Proxy
// 实现:与目标对象一样的接口(即上文例子的Subject接口)
public final class $Proxy0 extends Proxy implements Subject {

// 构造函数
public ProxySubject(InvocationHandler invocationhandler)   
    {   
        super(invocationhandler);   
    }  

 // buybuybuy()是目标对象实现接口(Subject)中的方法
 // 即$Proxy0类必须实现
 // 所以在使用动态代理类对象时,才可以调用目标对象的同名方法(即上文的buybuybuy())
 public final void buybuybuy() {
        try {
            super.h.invoke(this, m3, null); 
            // 该方法的逻辑实际上是调用了父类Proxy类的h参数的invoke()
            // h参数即在Proxy.newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h)传入的第3个参数InvocationHandler对象
            // 即调用了调用处理器的InvocationHandler.invoke()
            // 而复写的invoke()利用反射机制:Object result=method.invoke(proxied,args)
            // 从而调用目标对象的的方法 ->>关注4
            return;
        } catch (Error e) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

总结

1.动态代理类实现了与目标类一样的接口,并实现了需要目标类对象需要调用的方法
2.该方法的实现逻辑 = 调用父类 Proxy类的 h.invoke()
其中h参数 = 在创建动态代理实例中newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h)传入的第3个参数InvocationHandler对象
3.在 InvocationHandler.invoke() 中通过反射机制,从而调用目标类对象的方法

参考资料:https://www.jianshu.com/p/5dc416ea58a2

上一篇下一篇

猜你喜欢

热点阅读