一些優(yōu)秀的開(kāi)源框架,總會(huì)給開(kāi)發(fā)者留一個(gè)后門(mén),方便實(shí)現(xiàn)對(duì)其中某一塊功能,根據(jù)公司自身生態(tài)進(jìn)行有效的擴(kuò)展,比如Neflix開(kāi)源的Hystrix,其實(shí)Hystrix的代碼寫(xiě)的真的很好,除了RxJava那部分晦澀的實(shí)現(xiàn)。
在Hystrix中的插件實(shí)現(xiàn)中,提供了5個(gè)擴(kuò)展點(diǎn),通過(guò)實(shí)現(xiàn)這些插件接口,可以很好的結(jié)合公司內(nèi)部框架進(jìn)行使用,比如數(shù)據(jù)埋點(diǎn)、動(dòng)態(tài)配置等等,下面以事件通知接口為例子,看看Hystrix是如何實(shí)現(xiàn)的。
public HystrixEventNotifier getEventNotifier() {
if (notifier.get() == null) {
// check for an implementation from Archaius first
Object impl = getPluginImplementation(HystrixEventNotifier.class);
if (impl == null) {
// nothing set via Archaius so initialize with default
notifier.compareAndSet(null, HystrixEventNotifierDefault.getInstance());
// we don't return from here but call get() again in case of thread-race so the winner will always get returned
} else {
// we received an implementation from Archaius so use it
notifier.compareAndSet(null, (HystrixEventNotifier) impl);
}
}
return notifier.get();
}
通過(guò)getEventNotifier方法獲取事件通知器,當(dāng)然了,一開(kāi)始是沒(méi)有初始化的,先嘗試使用getPluginImplementation方法,看看能不能拿到,拿不到就使用本地默認(rèn)的實(shí)現(xiàn)。
private <T> T getPluginImplementation(Class<T> pluginClass) {
T p = getPluginImplementationViaProperties(pluginClass, dynamicProperties);
if (p != null) return p;
return findService(pluginClass, classLoader);
}
這里,Hystrix還提供了另外一種實(shí)現(xiàn),很簡(jiǎn)單,就不解釋了,這里的重點(diǎn)是findService實(shí)現(xiàn)。
private static <T> T findService(Class<T> spi, ClassLoader classLoader) throws ServiceConfigurationError {
ServiceLoader<T> sl = ServiceLoader.load(spi, classLoader);
for (T s : sl) {
if (s != null)
return s;
}
return null;
}
其實(shí),這里的實(shí)現(xiàn)是使用Java內(nèi)置的SPI機(jī)制,SPI是什么?
SPI 全稱(chēng)為 Service Provider Interface),是一種服務(wù)提供發(fā)現(xiàn)機(jī)制,通過(guò)ServiceLoader類(lèi)的load方法,可以自動(dòng)找到實(shí)現(xiàn)對(duì)應(yīng)接口的實(shí)現(xiàn)類(lèi),為了更清晰的了解其中原理,可以去看看load方法的源碼實(shí)現(xiàn)。
一個(gè)大概的過(guò)程是:load方法會(huì)嘗試在classpath下META-INF/services/文件夾下查找一個(gè)文件,其中文件名是接口的全限定名。

如上圖所述,CacheKeyFilter是一個(gè)接口,在這個(gè)文件中,需要指定接口實(shí)現(xiàn)類(lèi)的全限定名。

當(dāng)然了,你也可以換行指定多個(gè),找到這些實(shí)現(xiàn)類(lèi)之后,會(huì)通過(guò)反射機(jī)制,即class.newInstance()進(jìn)行實(shí)例化,這就要求實(shí)現(xiàn)類(lèi)需要有無(wú)參構(gòu)造函數(shù),
通過(guò)這種方式,我們就可以愉快的對(duì)開(kāi)源框架所提供的接口進(jìn)行擴(kuò)展,加入各種騷操作,還不動(dòng)手試試?