再聊代理模式前,我们先看下什么场景下我们会需要用到代理模式,以及为什么要用?
举个我们代码中常见的例子:
产品需求:我们需要看到一些指定接口的请求数据,比如请求总耗时时间,上报给服务器,用于了解用户的使用体验。
那我们开始撸代码了
class LoginRequest {
fun login() {
val startTime = System.currentTimeMillis()
// ...省略发起登陆请求的代码...
val endTime = System.currentTimeMillis()
//上报接口请求的开始时间和结束时间给服务器
ReportService.report(startTime,endTime,"urlLogin")
}
}
class HomePageRequest {
fun homePageData() {
val startTime = System.currentTimeMillis()
// ...省略发起首页数据请求的代码...
val endTime = System.currentTimeMillis()
//上报接口请求的开始时间和结束时间给服务器
ReportService.report(startTime,endTime,"urlHomePage")
}
}
很明显,上面的写法有2个问题,
1:请求耗时的代码和上报日志的代码完全重复,我们需要在每一个上报的请求中都重复此代码.
2:获取请求耗时的代码和上报的代码,和请求的代码在业务上毫无关联,耦合太高.
为了将上报框架代码和请求业务代码解耦,代理模式就派上用场了!
简单说下什么是代理模式:代理模式的本质就是在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。了解了代理模式的本质,我们再写改造一下上面的例子也加深理解。
interface ILoginInterface{
fun login()
}
class LoginRequest : ILoginInterface{
override fun login() {
// ...省略发起登陆请求的代码...
}
}
class LoginRequestProxy(private val realLogin:LoginRequest):ILoginInterface{
override fun login() {
val startTime = System.currentTimeMillis()
realLogin.login()
val endTime = System.currentTimeMillis()
//上报接口请求的开始时间和结束时间给服务器
ReportService.report(startTime,endTime,"urlLogin")
}
}
通过代码我们可以发现,LoginRequest
类中只有登陆请求的业务代码了,获取请求耗时和上报的代码都被我们放置在了LoginRequestProxy
中了,这样我们就做到了上报框架代码和业务代码的解耦。这种写法就是静态代理
.
但上面的写法也有个问题在于:我们需要给每个需要解耦、需要被代理的类都去创建一个xxxProxy
代理的类,这个工作不仅繁琐,同时还增加了维护成本,后续只要我们新增一个接口请求,都要创建一个代理类,实现相同的接口。这听着都让人头大~ 那有没有一劳永逸的办法呢,让我们不用每次都创建一个新的代理类?那就是我们下面要说到的动态代理
了.
动态代理:就是我们不事先为每个原始类编写代理类,而是在运行的时候,动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。那如何实现动态代理呢?
在java中使用是很方便的,系统已经给我们提供了Api,下面就让我们看下如何使用吧。
interface IRequestInterface {
fun request(url: String)
}
class LoginRequest : IRequestInterface {
override fun request(url: String) {
// ...省略发起登陆请求的代码...
}
}
//创建代理类的handler
class InvokeHandler(private val instance: IRequestInterface, val url: String) : InvocationHandler {
private var startTime by Delegates.notNull()
private var endTime by Delegates.notNull()
override fun invoke(proxy: Any, method: Method, args: Array?): Any? {
if (method.name.equals("request")) {
startTime = System.currentTimeMillis()
}
//这里需要注意 invoke接收的是可变参数,这里不可以直接传递args,要变成可变参数
val result = method.invoke(instance, *(args ?: emptyArray()))
when (method.name) {
"request" -> {
endTime = System.currentTimeMillis()
report(startTime, endTime, url)
}
}
return result
}
private fun report(startTime: Long, endTime: Long, url:String) {
ReportService.report(startTime,endTime,url)
}
}
//使用代码
class Test {
companion object {
@JvmStatic
fun main(args: Array) {
val proxy = createProxy(LoginRequest(),"urlLogin")
proxy.request("urlLogin")
}
private fun createProxy(instance: IRequestInterface,url: String): IRequestInterface {
val classLoader = instance::class.java.classLoader
val interfaces = instance::class.java.interfaces
val invokeHandler = InvokeHandler(instance,url)
return Proxy.newProxyInstance(
classLoader,
interfaces,
invokeHandler
) as IRequestInterface
}
}
}
我们可以发现,我们把请求耗时、上报的功能都放到了InvokeHandler
类的invoke方法中去处理,后续如果我们新增接口,也只需通过Proxy.newProxyInstance
动态创建一个代理类就可以了,而无需我们在项目中手动创建xxxProxy
类了