上一节咱们讲了一些Retrofit的概览,这一节咱们主要来讲一下代理模式。有同窗可能要问,这不是Retrofit的源码分析吗,怎么都第二节了还不分析源码呢?其实Retrofit这个框架中应用了不少的设计模式,其中最重要的就是动态代理模式。若是咱们要理解并掌握Retrofit,那么就必须先掌握代理模式。代理模式主要分为两种,静态代理和动态代理,下面咱们来细细的说明一下。java
package com.blackflagbin.frameanalysis.staticproxy;
//抽象日志类
public abstract class AbstractLogger {
abstract public void log();
}
复制代码
package com.blackflagbin.frameanalysis.staticproxy;
//真实操做日志类,继承AbstractLogger
public class RealLogger extends AbstractLogger {
@Override
public void log() {
System.out.println("show some log");
}
}
复制代码
package com.blackflagbin.frameanalysis.staticproxy;
//代理日志类,包含一个真实日志类的实例,在打印日志以前校验权限
public class ProxyLogger extends AbstractLogger {
private AbstractLogger mLogger;
public ProxyLogger(AbstractLogger logger) {
mLogger = logger;
}
private boolean checkPermission() {
return true;
}
@Override
public void log() {
if (checkPermission()) {
mLogger.log();
} else {
System.out.println("you have no access");
}
}
}
复制代码
上面三个类分别是抽象日志类、真实日志类、代理日志类。抽象日志类定义了一个打印日志的接口,真实日志类继承了抽象日志,并实现的这个打印日志的方法。这个时候,咱们想要在打印日志前加上权限校验,又不想直接修改咱们的真实日志类,那么就须要使用的静态代理模式。为了实如今打印日志前校验权限的功能,咱们建立了一个新类,代理日志类,这个类一样继承了抽象日志类,关键的是包含了一个真实日志类的引用对象。在这个代理类中的log方法中,经过在调用真实日志引用对象的log方法以前加入权限校验,从而实现了上述的功能。react
动态代理与静态代理最大的区别在于动态。那么问题来了,这个动态体如今哪里,又该如何去理解? 这个动态关键在于代理类的建立时机。静态代理中的代理类在咱们运行程序以前是必需要存在的,而动态代理中的代理类则是在程序运行时建立的。前半句很好理解,代理类的代码确定是先存在,而后才能运行,这个逻辑很符合咱们日常的开发模式。问题就在于后半句,代理类在运行时建立,运行时如何建立代理类?这是否是很反逻辑?代码都没有,怎么来根据咱们的需求来代理真实的被代理的对象?诸位稍安勿躁,咱们接下来会对动态代理进行详细的解释。 动态代理有两种实现方式:设计模式
package com.blackflagbin.frameanalysis.dynamicproxy;
//日志接口(动态代理不一样于静态代理,只能使用接口)
public interface ILogger {
void log();
}
复制代码
package com.blackflagbin.frameanalysis.dynamicproxy;
//真实日志类,实现日志接口
public class RealLogger implements ILogger {
@Override
public void log() {
System.out.println("show some log");
}
}
复制代码
package com.blackflagbin.frameanalysis.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyHandler implements InvocationHandler {
//被代理对象(即目标对象)的实例,在打印日志这个例子中对应RealLogger的实例
private final Object mTarget;
public ProxyHandler(Object target) {
mTarget = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (checkPermission()) {
System.out.println("you have access");
//被代理对象(即目标对象)方法的调用
return method.invoke(mTarget, args);
} else {
System.out.println("you have no access");
return null;
}
}
//建立实际的代理对象
public Object getProxyInstance() {
return Proxy.newProxyInstance(mTarget.getClass().getClassLoader(), mTarget.getClass().getInterfaces(), this);
}
private boolean checkPermission() {
return true;
}
}
复制代码
package com.blackflagbin.frameanalysis.dynamicproxy;
//测试类
public class Test {
public static void main(String[] args) {
ProxyHandler proxyHandler = new ProxyHandler(new RealLogger());
ILogger proxy = (ILogger) proxyHandler.getProxyInstance();
proxy.log();
}
}
/*
最终打印结果:
you have access
show some log
*/
复制代码
日志接口和真实日志类没什么可说的,跟静态代理同样,只不过由于动态代理必需要使用接口因此把抽象类换成了接口。 动态代理实现打印日志这个例子的关键在于ProxyHandler这个类。咱们知道,在静态代理中,对原有功能进行扩展或修改的代码实现是在静态代理类中定义的。也就是在ProxyLogger中添加额外的权限校验方法,并修改打印日志的流程。那么问题来了,在动态代理中,动态代理类是在程序运行时生成的,咱们并无事先声明一个动态代理类,这个对原有功能进行扩展或修改的代码实现究竟要放在哪里? 问题的答案在这个打印日志的例子里已经很明确了,对原有功能进行扩展或修改的代码实现放在了一个类中,这个类实现了InvocationHandler这个接口。咱们经过对invoke这个方法的修改来修改被代理对象的方法实现,经过在invoke方法中的method.invoke(mTarget, args)以前或以后插入咱们想要的逻辑来增对原有功能进行扩展。 在这个ProxyHandler类中,咱们还添加了一个getProxyInstance()方法来建立代理类对象。经过Proxy.newProxyInstance(mTarget.getClass().getClassLoader(), mTarget.getClass().getInterfaces(), this)来建立代理类的实例是固定的写法,newProxyInstance须要传入三个参数,分别是类加载器、接口数组和实现InvocationHandler的类的实例对象。 咱们再来总结一下。不管是静态代理仍是动态代理,它们的本质都是代理对象包含一个被代理对象的实例,从而对被代理对象的原有功能进行扩展或修改。最大的区别是代理类的建立时机不一样,静态代理必须在程序运行前写好代理类;而动态代理的代理类则不须要咱们手动提早写好,它会在运行时建立相应的代理类。 值得再次强调的是,虽然动态代理不须要咱们在代码中实现代理类,可是对原有功能进行扩展或修改的代码实现是必须提早写好的。这个很好理解,若是开发人员都不写清楚要如何对原有功能进行扩展或修改,计算机又怎么知道呢?因此对原有功能进行扩展或修改的代码实现就必须提早写好,问题是这些代码要放在那里,为了解决这个问题,Java提供了一个InvocationHandler的接口,咱们只要把相应的代码放到这个接口的实现类中便可。生成的代理对象在调用相应的方法时,实际上调用的是invoke这个方法,从而实现了对被代理对象的原有功能进行扩展或修改。 最后,我再贴上一些代码。api
package com.zhidian.cloudforpolice.common.http
import com.zhidian.cloudforpolice.BuildConfig
import com.zhidian.cloudforpolice.common.entity.http.*
import com.zhidian.cloudforpolice.common.entity.http.Unit
import io.reactivex.Observable
import retrofit2.http.*
/**
* Created by blackflagbin on 2018/1/27.
*/
interface ApiService {
//登陆
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}account/login.do")
fun login(@Field("username") userName: String, @Field("password") pwd: String, @Field("clientType") clientType: Int): Observable<HttpResultEntity<UserEntity>>
//登出
@POST("${BuildConfig.EXTRA_URL}account/logout.do")
fun logout(): Observable<HttpResultEntity<Any>>
//获取小区列表
@GET("${BuildConfig.EXTRA_URL}community/name/list")
fun getCommunityList(): Observable<HttpResultEntity<List<CommunityEntity>>>
//获取首页信息
@GET("${BuildConfig.EXTRA_URL}count/communityStatistic/{communityId}.do")
fun getMainData(@Path("communityId") communityId: Int): Observable<HttpResultEntity<MainEntity>>
//根据小区id获取小区楼幢列表
@GET("${BuildConfig.EXTRA_URL}community/block/name/list/{communityId}")
fun getBuildingList(@Path("communityId") communityId: Int): Observable<HttpResultEntity<List<BuildingEntity>>>
//根据楼幢id获取楼栋下的房间列表
@GET("${BuildConfig.EXTRA_URL}community/block/detail/{blockId}")
fun getRoomList(@Path("blockId") blockId: Int): Observable<HttpResultEntity<BuildingInfoEntity>>
//根据单元id获取单元下楼层列表
@GET("${BuildConfig.EXTRA_URL}community/unit/query/{unitId}")
fun getFloorList(@Path("unitId") unitId: Int): Observable<HttpResultEntity<Unit>>
//根据房间id获取房间详情
@GET("${BuildConfig.EXTRA_URL}community/room/detail/{roomId}")
fun getRoomInfo(@Path("roomId") roomId: Int): Observable<HttpResultEntity<RoomInfoEntity>>
//根据条件查询,获取符合条件的居民列表
@GET("${BuildConfig.EXTRA_URL}community/resident/list/{pageNo}/{limit}")
fun getSearchedPersonList(
@Path("pageNo") pageNo: Int, @Path(
"limit") limit: Int, @QueryMap map: Map<String, String>): Observable<HttpResultEntity<PersonEntity>>
//根据用户id获取关联房屋列表
@GET("${BuildConfig.EXTRA_URL}community/resident/room/list/{userId}")
fun getRelatedRoomList(@Path("userId") userId: Int): Observable<HttpResultEntity<List<RelatedRoomEntity>>>
//获取楼幢详情
@GET("${BuildConfig.EXTRA_URL}count/blockStatistic/{buildingId}.do")
fun getBuildingDetail(@Path("buildingId") buildingId: Int): Observable<HttpResultEntity<BuildingDetailEntity>>
//获取一级标签
@GET("${BuildConfig.EXTRA_URL}user/label/parent/list")
fun getFirstLabel(): Observable<HttpResultEntity<List<LabelItemEntity>>>
//获取二级标签
@GET("${BuildConfig.EXTRA_URL}user/label/child/list/{parentId}")
fun getSecondLabel(@Path("parentId") parentId: Int): Observable<HttpResultEntity<List<LabelItemEntity>>>
//修改密码
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}account/password/update")
fun changePwd(@Field("password") oldPwd: String, @Field("newPsw") newPwd: String): Observable<HttpResultEntity<Any>>
//门禁在线
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}device/query")
fun getDeviceList(@Field("communityId") communityId: Int, @Field("pageNo") pageNo: Int, @Field("limit") limit: Int): Observable<HttpResultEntity<DoorEntity>>
//获取门禁状态
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}device/selectDeviceStatusCount")
fun getDeviceStatusList(@Field("communityId") communityId: Int): Observable<HttpResultEntity<List<DeviceStatusItem>>>
//根据条件查询,获取警情处理规范列表
@GET("${BuildConfig.EXTRA_URL}police/handle/norm/list/{pageNo}/{limit}")
fun getPoliceHandleList(
@Path("pageNo") pageNo: Int, @Path(
"limit") limit: Int, @QueryMap map: Map<String, String>): Observable<HttpResultEntity<PoliceHandleEntity>>
//根据条件查询,获取重点人员预警列表
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}police/person/query")
fun getPersonPreWarningList(
@FieldMap map: Map<String, String>): Observable<HttpResultEntity<PersonPreWarningEntity>>
//根据条件查询,获取触警人员预警列表
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}police/person/queryContactPolice")
fun getAttackPreWarningList(
@FieldMap map: Map<String, String>): Observable<HttpResultEntity<PersonPreWarningEntity>>
//根据条件查询,获取重点房屋预警列表
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}police/room/query")
fun getRoomPreWarningList(
@FieldMap map: Map<String, String>): Observable<HttpResultEntity<RoomPreWarningEntity>>
//获取行为轨迹
@GET("${BuildConfig.EXTRA_URL}user/info/live/history/{userId}")
fun getMovePath(
@Path("userId") userId: String): Observable<HttpResultEntity<List<MovePathItemEntity>>>
//开门
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}device/operate/1")
fun openLock(
@Field("devicdId") deviceId: String): Observable<HttpResultEntity<Any>>
//更新人员预警状态
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}police/person/updStatu")
fun updatePersonPreWaning(
@Field("id") id: String, @Field("statu") status: Int, @Field("processContent") processContent: String): Observable<HttpResultEntity<Any>>
//更新房屋预警状态
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}police/room/updStatu")
fun updateRoomPreWaning(
@Field("id") id: String, @Field("statu") status: Int, @Field("processContent") processContent: String): Observable<HttpResultEntity<Any>>
//获取二维码返回结果
@FormUrlEncoded
@POST("api/qrcode/parsingcode")
fun getQrCodeResult(
@Field("code") qrcode: String): Observable<HttpResultEntity<String>>
}
复制代码
这是个Kotlin写的接口类,用过Retrofit的同窗应该很清楚,使用Retrofit进行网络请求,首先就是要建立一个网络请求接口类。数组
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://......../")
.build();
ApiService service = retrofit.create(ApiService.class);
复制代码
看到**ApiService service = retrofit.create(ApiService.class)**有没有很熟悉的感受?聪明的小伙伴看到这里应该就会明白了,没错,这其实就是经过动态代理建立了一个代理对象。咱们只是写了一个网络接口类,里面什么都没实现,为何就能够正确的请求网络并包装返回的数据结果?若是你从上到下把这篇文章看完了,即便你如今还并不清楚里面具体的代码细节,但有一点你会很是明确:**咱们写的ApiService这个接口类并不具备访问网络并包装返回数据结果的功能,是Retrofit经过动态代理的方式为咱们生成了一个代理对象,为咱们的接口方法扩展了网络访问的功能。**理解这一点,对咱们后续的源码分析很是重要。bash