Java动态代理InvocationHandler的一点感悟

目录

初衷

动态代理

实例讲解

总结


初衷

在研究Retrofit源码中,在创建网络请求接口实例时,就是通过Java动态代理模式,动态生成网络请求接口的代理类,并将代理类的实例创建交给了InvocationHandler来具体生成对应平台的代理对象。对应的代码如下:

public <T> T create(final Class<T> service) {
    //。。。。代码省略
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, Object... args)
              throws Throwable {
            //。。。。代码省略
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

其中使用Proxy.newProxyInstance来创建了一个代理类实例,那么对应的这个InvocationHandler怎么用呢?这个动态代理到底是怎么回事呢?研究下

动态代理

  • 基本概念

代理类在程序运行时创建的代理方式。与静态代理的区别在于静态代理的代理类是编译之前就存在了,像AIDL服务,其中的代理类在我们运行服务之前就借助Android Studio自动生成。

  • 优点

动态代理用于在编译的时候无法知道,只有在运行的时候才知道。像在Retrofit中创建网络请求接口的时候,并不知道是Android、iOS或者Java在使用,所以就需要设置CallAdapter来将设置的网络请求接口转换成对应平台的Http请求。这样开发者不需要手动去做适配,直接通过动态代理就可以完成这个适配过程。

优点在于可以对代理类的所有方法统一处理,不需要单独修改

1)InvocationHandler

可以理解为监听接口的方法的调用,可以在方法调用的时候,追加一些其他操作,查看源码如下

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

我们发现其中里面有三个参数

对应的参数 参数对应的含义
proxy 调用方法的代理实例
method 代理实例上调用的接口方法,包括若有父类,父类中声明的接口方法
args 传入代理实例上调用的接口方法的参数
返回值 就是上述的method的返回值。如果是基本数据类型,则返回的是其包装类的类型

2)Proxy.newProxyInstance 

动态生成给定接口的代理类。注意这里仅仅只针对接口类。

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

其中三个参数的含义如下:

对应的参数 参数对应的含义
loader 用来去加载代理对象的类加载器
interfaces 动态代理类需要实现的接口集合
h 可以理解为监听动态代理方法的调用。
返回值 根据上面三个参数生成的代理实例

实例讲解

我们定义了一种X付宝的支付方式,需要在支付方法在调用前增加安全校验和在支付结束增加支付成功的提示。那么在不修改原X付宝的支付方法的前提下,利用动态代理来实现该功能。

  • 定义支付接口
public interface Pay {
    Pay pay();
}

注意这里的方法的返回值要保持一致,否则在InvocationHandler的invoke()的返回值的要注意处理不同。

  • 定义X付宝支付方式
public class XliPay implements Pay {
    @Override
    public Pay pay() {
        System.out.println("正在使用X付宝支付");
        return this;
    }
}
  • 创建一个XliPay的代理类,在invoke方法的时候增加我们的校验检查
public class PayAccount<T> {

    public void before() {
        System.out.println("代理中。。。。检查支付方式是否安全。。。。");
    }

    public void after() {
        System.out.println("代理中。。。。成功完成支付。。。。。");
    }

    public T create(final Class<T> serviceInterface, final Class<XliPay> proxyInstance) {
        return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class[]{serviceInterface}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                before();
                Object obj = method.invoke(proxyInstance.getConstructor().newInstance(), args);
                after();
                return obj;
            }
        });
    }
}
  • 运行执行pay()看结果
  public static void main(String[] args) {
        PayAccount<Pay> payPayAccount = new PayAccount<>();
        Pay pay = payPayAccount.create(Pay.class, XliPay.class);
        pay.pay();
}

查看结果如下: 

代理中。。。。检查支付方式是否安全。。。。
正在使用X付宝支付
代理中。。。。成功完成支付。。。。。

 这样就可以直接在不用修改XliPay的pay()就完成了我们想要的效果。并且我们可以任意调整这几个方法的调用顺序。

总结

动态代理就是在调用委托类的方法的时候,可以在改变原有类的情况下,动态去替换原来的实现方法或者对其进行扩展。

但是动态代理最根本的就是利用了java的反射,所以对效率上是有一点点影响的。

猜你喜欢

转载自blog.csdn.net/nihaomabmt/article/details/87864675
今日推荐