在之前空对象模式一文中,讨论约束空对象方法调用时,提到可以使用 动态代理 实现。复用前文的数据模型,现在就来实现这个方式。而动态代理的具体运行逻辑详情,将在以后文章单独进行源码剖析。
和静态代理一样,动态代理也需要把所有行为抽象化,于是把以前写在 User 的行为全部抽象到接口 IUser 。
interface IUser{
fun getUserId(): String
fun getRoomId(): String
fun getRemark(): String
}
然后 User 模型实现该抽象接口
class User(private val userId: String,
private val roomId: String,
private val nickname: String) : IUser {
override fun getUserId(): String {
return userId
}
override fun getRoomId(): String {
return roomId
}
override fun getRemark(): String {
return nickname
}
}
实现动态代理的关键是实现 InvocationHandler 。使用动态代理,是为了把所有方法代理到同一实例中,所以实现 InvocationHandler 时还需要提供有参构造方法,让外部传入接收委托的实例。
不过,这次并不需要委托任何行为,而只是通过动态代理这个中转,以抛出异常的方式阻止实例里所有方法的调用。由以下代码可见,该中介类无需保存被委托类实例,只是在实现方法里抛异常阻止调用。
class Handler : InvocationHandler {
// 实现唯一的抽象方法
override fun invoke(proxy: Any, method: Method, args: Array<out Any>): Any {
// 抛出异常
throw IllegalAccessException("Access to this method is denied.")
}
}
实现中介类 InvocationHandler 之后就可以创建实例。按照惯例,空对象单例对象一般和其模型放在一起。
class User(private val userId: String,
private val roomId: String,
private val nickname: String) : IUser {
.....
companion object {
val defUser by lazy {
// 创建空对象实例,这个实例里所有方法已被代理
Proxy.newProxyInstance(
IUser::class.java.classLoader,
arrayOf(IUser::class.java),
Handler()) as IUser
}
}
}
这个被代理的实例使用方式和普通对象无异
object ProxyRunner {
@JvmStatic fun main(args: Array<String>) {
User.defUser.getRemark()
}
}
只在运行时走代理逻辑,然后主动抛出预定实现的异常:
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException at com.sun.proxy.$Proxy0.getRemark(Unknown Source) at proxy.ProxyRunner.main(ProxyRunner.java:6) Caused by: java.lang.IllegalAccessException: Access to this method is denied. at proxy.Handler.invoke(Handler.kt:8) ... 2 more
上一篇
5个线程先打印Hello再打印world