注册

Java静态代理和动态代理


  • 前言



再开始之前我们先不使用任何代理来实现一个网络请求的流程。


定义一个请求的接口:


public interface Request {
void request();
}

使用OkHttp来实现这个接口


public class OkHttpImpl implements Request {
@Override
public void request() {
System.out.println("OkHttp请求成功");
}
}

现在我们的网络请求已经写好了,我们测试一下:


Request request = new OkHttpImpl();
request.request();

输出: OkHttp请求成功

看起来挺好用的,但是项目经理是个老程序员了,没有用过OkHttp,非要说Volley比OkHttp好用,让你把所有网络请求换成Volley框架


我们使用Volley来实现Request接口


public class VolleyImpl implements Request{
@Override
public void request() {
System.out.println("Volley请求成功");
}
}

重新测试测试一下:


Request request = new VolleyImpl();
request.request();

输出: Volley请求成功

现在项目经理又来了,说:“你网络请求怎么连个加载框都有没?”,这个时候又得去改代码了,但是公司网络框架已经封住好了,不让随便修改,这个时候没有办法了,只能这样写了:


showDialog(); //显示加载进度条

Request request = new VolleyImpl();
request.request();

hideDialog(); //隐藏加载进度条

看起来代码没问题,但是项目中有上百个网络请求,难道每次写网络请求都要手动加上进度条的代码吗?这个时候你去问项目经理,项目经理说:“你去看看Java静态代理和动态代理,或许能找到答案~”。



  • 静态代理



在这里插入图片描述 看起来用户不需要直接访问网络框架了,而是先访问一个代理类,由代理类去执行网络请求,那我们先新建一个代理类:


public class RequestProxy implements Request {

private final Request mRequest;

public RequestProxy(Request request) {
mRequest = request;
}

public void before(){
System.out.println("开始请求");
showDialog(); //显示加载进度条
}

public void after(){
System.out.println("请求完成");
hideDialog(); //隐藏加载进度条
}

@Override
public void request() {
before();
mRequest.request();
after();
}
}

现在我们来测试一下:


Request request = new VolleyImpl();
RequestProxy proxy = new RequestProxy(request);
proxy.request();

输出:
开始请求
Volley请求成功
请求完成

静态代理优点:



  1. 可以在代理类中对目标类进行扩展。
  2. 用户只需要使用代理类的方法,不需要关心真正实现方法。
  3. 用户可以通过代理类实现与真正逻辑的解耦。

静态代理的缺点:



  1. 如果增加一个接口,还需要重新写一个代理类。


  • 动态代理



动态代理不需要写代理类,能很好的弥补静态代理的缺点


我们需要使用Java内部给我们提供好的**Proxy.newProxyInstance()**方法


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


newProxyInstance方法需要传入三个参数:



  1. loader: 类加载器
  2. interfaces: 要代理的接口
  3. InvocationHandler: 会回调动态代理的消息

我们先来实现一下动态代理:


Request request = new VolleyImpl();
Object o = Proxy.newProxyInstance(request.getClass().getClassLoader(), new Class[]{Request.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("请求前");
method.invoke(request, args);
System.out.println("请求后");
return null;
}
});

((Request) o).request();

输出:
请求前
Volley请求成功
请求后


  • 动态代理代码解析



我们先把要代理的接口传入到newProxyInstance方法中,并拿到代理对象“o”。


Object o = Proxy.newProxyInstance(request.getClass().getClassLoader(), new Class[]{Request.class}, new InvocationHandler() {})

我们可以把代理类强转成我们要代理的接口,然后直接调用方法


((Request) o).request();

这样代理类的invoke()方法就会被回调,我们看一下invoke()的三个参数:


@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

return null;
}


  1. proxy: 代理类的对象
  2. method: 代理类调用的方法
  3. args: 代理类调用方法传的参数

既然回调方法中有method参数了,我们就可以利用反射直接掉用method.invoke(request, args)来调用方法了,同时我们也可以在调用方法前后加上要扩展的代码。


作者:lvkaixuan
链接:https://juejin.cn/post/6983846546844418062
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册