先導(dǎo)知識(shí)
?我們所說的委托,其實(shí)就是一種設(shè)計(jì)模式:有兩個(gè)對(duì)象參與處理同一個(gè)請(qǐng)求,接受請(qǐng)求的對(duì)象將請(qǐng)求委托給另一個(gè)對(duì)象來處理。換一種方法來說就是,我們(調(diào)用方)操作的對(duì)象不會(huì)自己去處理邏輯,而會(huì)把工作委托給另外一個(gè)輔助對(duì)象去處理。委托模式使得我們可以使用聚合來替代繼承,更加靈活。舉個(gè)簡(jiǎn)單的例子,朋友圈的微商賣貨就是一種委托模式,“微商”代替廠家賣貨,廠家“委托”他們進(jìn)行銷售。在這種關(guān)系里,“微商”就相當(dāng)于“代理類”,而廠家則是“委托類”。
?Java在語法層面上是沒有支持委托模式的,但是可以通過代理模式來實(shí)現(xiàn)委托。Java的代理模式分為靜態(tài)代理和動(dòng)態(tài)代理,我們簡(jiǎn)單過一下。
靜態(tài)代理
?所謂靜態(tài)代理,是指代理類在編譯時(shí)就已經(jīng)創(chuàng)建好了。
public class ProxyDemo {
public static void main(String[] args) {
BaseImp imp = new BaseImp();
Proxy proxy = new Proxy(imp);
proxy.doSomething();
}
}
interface Base {
void doSomething();
}
//委托類
class BaseImp implements Base {
@Override
public void doSomething() {
System.out.println("BaseImp do something...");
}
}
//代理類
class Proxy implements Base {
private final Base baseImp;
public Proxy(Base base) {
this.baseImp = base;
}
@Override
public void doSomething() {
baseImp.doSomething();
}
}
?在這里,Proxy就是代理類,BaseImp則是委托類。當(dāng)我們調(diào)用Proxy的方法時(shí)執(zhí)行邏輯時(shí),Proxy就會(huì)去調(diào)用BaseImp實(shí)例來處理。在靜態(tài)代理中,代理類和委托類擁有共同的父類或者父接口。這里要提一嘴的是,關(guān)于兩個(gè)類的叫法問題。我們調(diào)用Proxy類,而Proxy再去調(diào)用(這里我理解為委托)BaseImp去做,而BaseImp卻叫做委托類,感覺是不是有點(diǎn)怪怪的(我覺得應(yīng)該叫被委托類更合適)。所以這時(shí)候我們可以反過來想,就是因?yàn)锽aseImp去委托了Proxy來代理它的事務(wù),所以它理所當(dāng)然就是委托方啦。當(dāng)然了,這里我們不用去糾結(jié),概念本身創(chuàng)造出來就是幫助我們來理解的,而不是讓我們鉆牛角尖的,所以我們清楚這個(gè)模式所要表達(dá)(傳遞)的意思就行。
?既然靜態(tài)代理在編譯時(shí)就產(chǎn)生對(duì)應(yīng)的字節(jié)碼文件,那也就意味著效率會(huì)比較高。但也是因此靜態(tài)代理只能為一個(gè)目標(biāo)對(duì)象服務(wù),如果目標(biāo)對(duì)象過多,就會(huì)對(duì)象產(chǎn)生較多的代理類。
動(dòng)態(tài)代理
?跟靜態(tài)代理不同的是,動(dòng)態(tài)代理的代理類是在運(yùn)行時(shí)生成的,我們看名字也看得出來。也就是說,動(dòng)態(tài)代理類是在程序運(yùn)行時(shí)由Java反射機(jī)制動(dòng)態(tài)生成的,我們不需要去編寫代理類的邏輯代碼。
?實(shí)現(xiàn)動(dòng)態(tài)代理的步驟為:
(1)定義公共接口及實(shí)現(xiàn)委托類。
(2)實(shí)現(xiàn)Java反射包的InvocationHandler接口,來創(chuàng)建代理類的調(diào)用處理器。
(3)動(dòng)態(tài)生成代理對(duì)象。
(4)通過代理對(duì)象調(diào)用方法。
public class DynamicProxyDemo {
public static void main(String[] args) {
BaseDynamicImpl dynamicImpl = new BaseDynamicImpl();
//通過委托類來構(gòu)造調(diào)用處理器
ProxyHandler proxyHandler = new ProxyHandler(dynamicImpl);
//通過Java反射包的Proxy類來動(dòng)態(tài)生成代理對(duì)象
BaseDynamic proxy = (BaseDynamic) Proxy.newProxyInstance(
BaseDynamicImpl.class.getClassLoader(),
BaseDynamicImpl.class.getInterfaces(),
proxyHandler);
//通過代理對(duì)象調(diào)用方法
proxy.doSomething();
}
}
//公共接口
interface BaseDynamic {
void doSomething();
}
//委托類
class BaseDynamicImpl implements BaseDynamic {
@Override
public void doSomething() {
System.out.println("BaseDynamicImpl do something...");
}
}
//代理類的調(diào)用處理器
class ProxyHandler implements InvocationHandler {
//一樣,我們需要持有共同的父類接口
private final BaseDynamic baseDynamic;
public ProxyHandler(BaseDynamic baseDynamic) {
this.baseDynamic = baseDynamic;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
return method.invoke(baseDynamic, objects);
}
}
?動(dòng)態(tài)代理涉及到兩個(gè)重要的Java API:
(1)java.lang.reflect.InvocationHandler:調(diào)用處理器接口,它的invoke方法用于集中處理在動(dòng)態(tài)代理類對(duì)象上的方法調(diào)用,通常在該方法中實(shí)現(xiàn)對(duì)委托類的代理訪問。
(2)java.lang.reflect.Proxy:Java動(dòng)態(tài)代理機(jī)制生成的所有動(dòng)態(tài)代理類的父類,提供了一組靜態(tài)方法來為一組接口動(dòng)態(tài)地生成代理類及其對(duì)象。
?很明顯,既然通過了反射代理方法,那么也就會(huì)意味著性能問題。但相應(yīng)的,動(dòng)態(tài)代理的優(yōu)勢(shì)也很明顯,可以顯著地減少代理類的數(shù)量,我們不需要每次都去為目標(biāo)對(duì)象創(chuàng)建相應(yīng)的代理類,使用起來更加靈活。Retrofit就是通過動(dòng)態(tài)代理對(duì)上層接口的封裝,讓我們可以用更加面向?qū)ο蟮倪壿?,輕松地訪進(jìn)行網(wǎng)絡(luò)操作。
Kotlin的委托模式
?說完了Java的委托,我們?cè)倩剡^頭來看看Kotlin的委托。
?作為極具創(chuàng)新的現(xiàn)代化語音,Kotlin直接在語法層面上支持了委托模式,那就意味著我們使用起來更加的方便了。Kotlin的委托分為類委托和委托屬性。
類委托
?我們還是用剛剛的例子:
interface Base {
fun doSomething()
}
//委托類
class BaseImpl : Base {
override fun doSomething() {
println("BaseImpl do something...")
}
}
//通過by關(guān)鍵字完成委托,Derived相當(dāng)于代理類
class Derived(base: Base) : Base by base
fun main(args: Array<String>) {
val base = BaseImpl()
val derived = Derived(base)
derived.doSomething()
}
?你會(huì)發(fā)現(xiàn),非常簡(jiǎn)單,我們僅僅通過by關(guān)鍵字,便將職責(zé)委托給了BaseImpl類,當(dāng)然我們自己也要實(shí)現(xiàn)Base接口,才能實(shí)現(xiàn)相對(duì)應(yīng)方法的委托。其實(shí)我們寫的時(shí)候也可以發(fā)現(xiàn),當(dāng)我們只寫到繼承Base接口還沒寫by的時(shí)候,IDE是會(huì)報(bào)錯(cuò)的,提示你沒有去重寫對(duì)應(yīng)的方法。但當(dāng)我們通過by關(guān)鍵字實(shí)現(xiàn)委托之后,IDE就正常了。而這么做的好處我們剛剛也說過了,通過委托來替代繼承是一個(gè)很好的選擇,而Kotlin又給予了我們語法層面的支持,簡(jiǎn)直不要太爽!
?比如我們要實(shí)現(xiàn)一個(gè)Set類,但是大部分邏輯都跟HashSet差不多,這個(gè)時(shí)候我們就可以利用委托來實(shí)現(xiàn)。我們只需要重寫自己需要的方法即可:
class MySet<T>(private val helperSet: HashSet<T>) : Set<T> by helperSet {
fun helloWorld() = println("Hello Kotlin World")
override fun isEmpty(): Boolean {
return false
}
}
?同樣,我們通過by把邏輯委托給了helperSet,除了我們自己重寫的isEmpty()方法,當(dāng)然這里沒有實(shí)際意義,只是為了演示,然后我們又添加了一個(gè)自己獨(dú)有的helloWorld()方法。這樣一來,我們就擁有了HashSet的全部能力以及我們自己獨(dú)有的方法,一個(gè)全新的數(shù)據(jù)結(jié)構(gòu)類就誕生了。
?既然by這么神奇,我們就來看看它背后是怎么實(shí)現(xiàn)的。我們先把文件編譯成Kotlin字節(jié)碼,再反編譯回來:
public final class MySet implements Set, KMappedMarker {
private final HashSet helperSet;
public final void helloWorld() {
String var1 = "Hello Kotlin World";
System.out.println(var1);
}
public boolean isEmpty() {
return false;
}
public MySet(@NotNull HashSet helperSet) {
Intrinsics.checkNotNullParameter(helperSet, "helperSet");
super();
this.helperSet = helperSet;
}
//省略其他方法...
}
?你會(huì)發(fā)現(xiàn),和我們剛剛Java的靜態(tài)代理一模一樣,只不過是Kotlin幫我們默默地把事情都做了(它真的,我哭死)。
委托屬性
?類委托的核心思想是將一個(gè)類的具體實(shí)現(xiàn)委托給另外一個(gè)類去完成,而委托屬性的核心思想是將一個(gè)屬性(字段)的具體實(shí)現(xiàn)委托給另外一個(gè)類去完成。先看下委托屬性的語法結(jié)構(gòu):
class PropertyProxy {
var p by Delegate()
}
?同樣的,通過by關(guān)鍵字來實(shí)現(xiàn),這意味著將p屬性的具體實(shí)現(xiàn)委托給Delegate類來完成。當(dāng)然這個(gè)時(shí)候,IDE會(huì)報(bào)錯(cuò),提示我們創(chuàng)建帶有g(shù)etValue和setValue方法的Delegate類,根據(jù)提示去創(chuàng)建這兩個(gè)方法即可,我們來具體實(shí)現(xiàn)一下:
class Delegate {
var propertyValue: Any? = null
operator fun getValue(proxy: PropertyProxy, property: KProperty<*>): Any? {
return propertyValue
}
operator fun setValue(proxy: PropertyProxy, property: KProperty<*>, any: Any?) {
propertyValue = any
}
}
?可以看到,我們重寫了getValue()和setValue()方法,并用operator關(guān)鍵字進(jìn)行聲明。我們直接看setValue()方法,第一個(gè)參數(shù)就是我們的代理類,也就是聲明該類(Delegate)的委托功能可以在哪個(gè)類中使用;第二個(gè)參數(shù)KProperty< * >是Kotlin的一個(gè)屬性操作類,可用于獲取各種屬性相關(guān)的值,當(dāng)前場(chǎng)景下用不上,而< * >表示我們不關(guān)心泛型的具體類型,類似于Java中的<?>。
?委托屬性的工作流程就是當(dāng)我們?nèi)ソoPropertyProxy類的p屬性賦值的時(shí)候,就會(huì)調(diào)用Delegate類的setValue方法,而當(dāng)我們獲取p屬性的值時(shí),就會(huì)去調(diào)用Delegate類的getValue方法。就是這么簡(jiǎn)單,真正的難點(diǎn)在于如何靈活應(yīng)用。
by lazy
?既然談到委托屬性,那總是繞不開by lazy的。相信大家也很熟悉,平常幾乎都用得飛起。by lazy其實(shí)是屬于延遲屬性,我們經(jīng)常用的lateinit也是(具體來說是通過它們來修飾),而延遲屬性就是委托屬性的一種。
?說回by lazy,其中l(wèi)azy并不是關(guān)鍵字,而是一個(gè)高階函數(shù),可以接收一個(gè)lambda表達(dá)式作為參數(shù),我們來看下面的例子:
class PropertyProxy {
var p by Delegate()
val str: String by lazy {
println("enter lazy()")
"Value"
}
}
fun main(args: Array<String>) {
val proxy = PropertyProxy()
println(proxy.str)
println("------")
println(proxy.str)
}
?新增一個(gè)str屬性,然后我們調(diào)用它,把結(jié)果打印出來:
enter lazy()
Value
------
Value
?我們會(huì)發(fā)現(xiàn),調(diào)用了兩次str屬性,而"enter lazy()"只打印了一次。這是因?yàn)閘azy函數(shù)只有在第一次調(diào)用時(shí)執(zhí)行整個(gè)Lambda表達(dá)式,而后調(diào)用則會(huì)直接返回第一次調(diào)用返回的結(jié)果。也就是說lazy()方法會(huì)把最后一行作為返回值,當(dāng)我們第一次調(diào)用str完成初始化之后,后面再調(diào)用的話就直接以最后一行作為返回值返回,這其實(shí)也是延遲屬性(懶加載)的奧義所在??聪滤脑创a:
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
?這里的actual是Kotlin的關(guān)鍵字,表示多平臺(tái)項(xiàng)目中的一個(gè)平臺(tái)相關(guān)實(shí)現(xiàn)。lazy()方法接收了一個(gè)lambda參數(shù),然后會(huì)返回一個(gè)Lazy對(duì)象,這里返回了SynchronizedLazyImpl實(shí)例。很明顯SynchronizedLazyImpl類是Lazy的子類。
?先看下Lazy,它是一個(gè)接口:
/**
* 表示具有延遲初始化的值.
*
* To create an instance of [Lazy] use the [lazy] function.
*/
public interface Lazy<out T> {
/**
* 獲取當(dāng)前 Lazy 實(shí)例的延遲初始化值.
* 初始化該值后,在此延遲實(shí)例(Lazy instance)的剩余生命周期內(nèi)不得更改.
*/
public val value: T
/**
* 如果此延遲實(shí)例的值已初始化,則返回“true”,否則返回“false”.
* 一旦此函數(shù)返回“true”,它將在此 Lazy 實(shí)例的剩余生命周期內(nèi)保持“true”.
*/
public fun isInitialized(): Boolean
}
?很簡(jiǎn)單,持有一個(gè)value字段,然后一個(gè)isInitialized()方法,官方注釋也寫得很清楚。這里的<out T>是表示Lazy在泛型T上是協(xié)變的,具體可以看Kotlin的協(xié)變和逆變。提一嘴,Google爸爸的注釋寫得都非常詳細(xì),一定要去看。接著詳細(xì)來看下SynchronizedLazyImpl類的實(shí)現(xiàn):
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this
override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
private fun writeReplace(): Any = InitializedLazyImpl(value)
}
?可以看到SynchronizedLazyImpl類實(shí)現(xiàn)了Lazy和Serializable接口,接收兩個(gè)參數(shù)initializer和lock,initializer就是我們的初始化邏輯函數(shù),lock則是鎖,用于獲取值時(shí)的同步操作,默認(rèn)為空。重載了Lazy接口的value(和isInitialized()方法),Lazy接口的value屬性用于獲取當(dāng)前Lazy實(shí)例的延遲初始化值,一旦初始化之后,就不能在改實(shí)例的剩余生命周期內(nèi)更改。所以重載的value屬性只有g(shù)et()方法,并沒有set()方法。
?然后自己還持有了一個(gè)私有的_value值,并設(shè)置了初始值:
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
?_value使用了@Volatile注解,相當(dāng)于Java的volatile關(guān)鍵字,具有可見性和有序性,因此一旦_value值修改了,其他線程就可以看到其最新的值。
?value屬性在get()時(shí),會(huì)先判斷當(dāng)前的_value還是不是初始值(UNINITIALIZED_VALUE),如果不是就說明已經(jīng)初始化過了,直接返回即可;否則就走初始化邏輯來初始化_value值,我們看到使用了synchronized方法(類似Java的synchronized關(guān)鍵字)來保證線程安全,然后又判斷了一次_v2 !== UNINITIALIZED_VALUE,進(jìn)行雙重檢驗(yàn)。最后執(zhí)行initializer()函數(shù)進(jìn)行初始化,更新_value,再進(jìn)行返回。整個(gè)邏輯其實(shí)很清晰。
?我們上面分析的這個(gè)lazy()方法其實(shí)是最常用的一個(gè),它還有另外兩個(gè)實(shí)現(xiàn):
public actual fun <T> lazy(lock: Any?, initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer, lock)
public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
when (mode) {
LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
}
?第一個(gè)其實(shí)就是把SynchronizedLazyImpl里的lock參數(shù)提到lazy()方法里來,本質(zhì)上和我們前面提到的沒區(qū)別,我們直接看第二個(gè)lazy()方法,多出來了一個(gè)LazyThreadSafetyMode類型的參數(shù),然后再根據(jù)不同模式去創(chuàng)建不同的實(shí)例。
?SYNCHRONIZED返回的也是我們剛剛分析的SynchronizedLazyImpl類;PUBLICATION則是SafePublicationLazyImpl類;而NONE是UnsafeLazyImpl類。其實(shí)我們通過名字也可以分辨出來,UnsafeLazyImpl是非線程安全的,而其他兩個(gè)是線程安全的,我們來簡(jiǎn)單看下SafePublicationLazyImpl的源碼:
private class SafePublicationLazyImpl<out T>(initializer: () -> T) : Lazy<T>, Serializable {
@Volatile private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// this final field is required to enable safe initialization of the constructed instance
private val final: Any = UNINITIALIZED_VALUE
override val value: T
get() {
val value = _value
if (value !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return value as T
}
val initializerValue = initializer
// if we see null in initializer here, it means that the value is already set by another thread
if (initializerValue != null) {
val newValue = initializerValue()
if (valueUpdater.compareAndSet(this, UNINITIALIZED_VALUE, newValue)) {
initializer = null
return newValue
}
}
@Suppress("UNCHECKED_CAST")
return _value as T
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
private fun writeReplace(): Any = InitializedLazyImpl(value)
companion object {
private val valueUpdater = java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater(
SafePublicationLazyImpl::class.java,
Any::class.java,
"_value"
)
}
}
?這邊邏輯判斷上和前面有些不同,我們可以看到initializer屬性也被加上了 @Volatile注解:
@Volatile private var initializer: (() -> T)? = initializer
?然后接著我們判斷完“value !== UNINITIALIZED_VALUE”如果不成立,也就是value還沒被初始化時(shí),我們?cè)偻伦?。這個(gè)時(shí)候,拿initializer來做判斷,“if we see null in initializer here, it means that the value is already set by another thread”,大意就是:假如我們這時(shí)發(fā)現(xiàn)initializer是空的,那么意味著value值已經(jīng)在別的線程被初始化了。為什么這么說呢,我們接下來看:
if (initializerValue != null) {
val newValue = initializerValue()
if (valueUpdater.compareAndSet(this, UNINITIALIZED_VALUE, newValue)) {
initializer = null
return newValue
}
}
return _value as T
?也就是說,如果initializer(initializerValue)為空,那么我們就直接跳過做返回操作了。那不為空的時(shí)候呢,我們是不是就得去初始化,進(jìn)到if邏輯里,先初始化拿到值(newValue),然后再通過valueUpdater的compareAndSet()做判斷,如果邏輯成立(我們先不管怎么成立),那我們就會(huì)把initializer賦值為null,完成初始化。這也就是為什么說在判斷的時(shí)候發(fā)現(xiàn)initializer為空,那么就意味著value值已經(jīng)在別的線程被初始化了的原因。那valueUpdater是啥?
companion object {
private val valueUpdater = java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater(
SafePublicationLazyImpl::class.java,
Any::class.java,
"_value"
)
}
?是Java并發(fā)包的AtomicReferenceFieldUpdater類,newUpdater是它的一個(gè)靜態(tài)方法,返回自身。那么其實(shí)上面的操作也就是通過AtomicReferenceFieldUpdater的CAS邏輯來保證了_value屬性的原子操作,因?yàn)锧Volatile是不具備原子性的。
?這也就意味著SafePublicationLazyImpl類是支持多個(gè)線程同時(shí)調(diào)用,并且可以在全部或部分線程上同時(shí)進(jìn)行初始化。但是,如果某個(gè)值已經(jīng)由另外一個(gè)線程初始化,則將返回該值,不再進(jìn)行初始化。
?UnsafeLazyImpl類因?yàn)槭欠蔷€程安全的,所以就是對(duì)value直接操作判斷,這里就不再分析了。總結(jié)下三種模式的區(qū)別:
SYNCHRONIZED => lazy()方法的默認(rèn)模式,初始化操作僅僅在首先調(diào)用的第一個(gè)線程上執(zhí)行,其他線程將引用緩存后的值。
PUBLICATION => 支持同時(shí)多個(gè)線程調(diào)用,并且可以在全部或者部分線程上同時(shí)進(jìn)行初始化。如果某個(gè)值已由另一個(gè)線程初始化,則將返回該值,而不執(zhí)行初始化。
NONE => 不是線程安全的。
結(jié)語
?本篇文章主要圍繞委托來簡(jiǎn)單介紹了Java的靜態(tài)代理和動(dòng)態(tài)代理,再到Kotlin的類委托和委托屬性,最后分析了by lazy延遲屬性。其實(shí)整體分析下來,我們會(huì)發(fā)現(xiàn)并沒有太多的難點(diǎn),Kotlin精妙的設(shè)計(jì)讓我們?cè)谔幚硎聞?wù)上更加簡(jiǎn)潔明了。