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请求成功
请求完成
静态代理优点:
- 可以在代理类中对目标类进行扩展。
- 用户只需要使用代理类的方法,不需要关心真正实现方法。
- 用户可以通过代理类实现与真正逻辑的解耦。
静态代理的缺点:
- 如果增加一个接口,还需要重新写一个代理类。
-
动态代理
动态代理不需要写代理类,能很好的弥补静态代理的缺点
我们需要使用Java内部给我们提供好的**Proxy.newProxyInstance()**方法
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
newProxyInstance方法需要传入三个参数:
- loader: 类加载器
- interfaces: 要代理的接口
- 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;
}
- proxy: 代理类的对象
- method: 代理类调用的方法
- args: 代理类调用方法传的参数
既然回调方法中有method参数了,我们就可以利用反射直接掉用method.invoke(request, args)来调用方法了,同时我们也可以在调用方法前后加上要扩展的代码。
作者:lvkaixuan
链接:https://juejin.cn/post/6983846546844418062
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。