背景
目前在做SDK时,网络层使用的框架是OkHttp + Retrofit,需要对外提供API方式的网络接口调用。
一般来说,后台都会有接口校验设计,需要在请求头包含部分加密参数进行验证。
我们的项目也是这样处理,外放的接口需要先调用登录接口获取Token,根据Token在请求头进行组合形成鉴权,才能成功通过后台的校验进行使用。
这时候想,如果每个接口都需要提前先获取Token,工作量非常大,于是想到了类似Java的AOP的面向切面处理方式,使用动态代理灵活在接口方法前插入获取Token逻辑。
而部分接口例如,获取验证码、登录相关的,并不需要Token,因此再自定义一个注解用于区别。
动态代理
要理解动态代理首先要理解代理,
- 代理:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象。
代理有什么用呢?举个例子,上大学偶尔舍友会不想上他自己的课,于是让我去代签名,此时我就成为了这个舍友的代理,替他去上课。
一般来说,我们使用聚合方式让代理类持有一个被代理类对象,即可实现代理,而此种方式一般称为静态代理。
在项目中我们有一个Repository实现了具体的网络接口请求,里面有许多接口方法,如下所示:
1 | public class Repository implements IRepository { |
目前我们想要有一个Repository的Proxy类,在每个接口前加入获取Token的逻辑,如果用静态代理来做,如下:
1 | public class RepositoryProxy implements IRepository { |
可以看到每个接口前都需要调用getToken,同样的逻辑需要反复实现,虽然效果达到了,但是由此写了很多重复的代码,因此静态代理也不适用于我们的需求。
于是使用动态代理来实现,这样就达到了预期的效果,如下:
1 | public class RepositoryInvocationHandler implements InvocationHandler { |
具体是实现InvocationHandler接口,覆写invoke方法,method.invoke(repository, args)即等价于调用IRepository的网络请求接口,在此方法前可以插入自定义逻辑。
在原本需要调用Repository的地方生成动态代理方式,如下:
1 | Repository repository = new Repository(); |
注解
注解(也被成为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。 ———摘自《Thinking in Java》
简单来说注解的作用就是将我们的需要的数据储存起来,在以后的某一个时刻(可能是编译时,也可能是运行时)去调用它。
在我们的项目中,针对部分接口不需要Token的,处理逻辑不同,于是通过自定义注解实现,如下:
1 | @Target(ElementType.METHOD) |
在Repository中,对需要获取Token的方法前加@NeedToken即可。最后修改InvocationHandler的逻辑,如下:
1 | public class RepositoryInvocationHandler implements InvocationHandler { |
另外,由于getToken一般也为一个接口方法,需要异步回调获取Token,需注意RepositoryInvocationHandler类不能写成单例,应该每个接口方法对应一个proxy,对应一个获取Token回调,否则会造成冲突。
Done.