一、dubbo spi效果簡單描述
比如,你寫了一個(gè)工具jar包,為了方便擴(kuò)展,代碼里以接口的方式調(diào)用,沒有具體的實(shí)現(xiàn)類,下層應(yīng)用只需要再mate-inf里以key-value對的形式指定好具體的實(shí)現(xiàn)類,然上層應(yīng)用depend一下就能以這個(gè)實(shí)現(xiàn)類運(yùn)行,達(dá)到很好的擴(kuò)展性效果。
一、dubbo為什么要自己實(shí)現(xiàn)spi呢?
1、因?yàn)?,dubbo需要一個(gè)接口提供多個(gè)實(shí)現(xiàn)類,然后通過注解的方式或者其他方式,指定使用哪個(gè)實(shí)現(xiàn)類,而java se 的spi不是key-value對的形式。
比如,它的/META_INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件內(nèi)容是:
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
http=com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
以properties的方式,對實(shí)現(xiàn)類提供了一個(gè)標(biāo)識(shí)符,然后通過@SPI("dubbo")這樣的方式,實(shí)現(xiàn)加載指定的實(shí)現(xiàn)類,而這個(gè)方式,是java自帶的spi做不到的。
2、dubbo的spi有adapter spi來達(dá)到根據(jù)不同參數(shù)使用不同實(shí)現(xiàn)類的動(dòng)態(tài)效果,有activate根據(jù)不同條件獲取不同實(shí)現(xiàn)類的效果。這些豐富的效果是javase spi不具備的。
3、dubbo的spi的ExtendsionFactory就是基于jdk的spi的。
4、dubbo的spi,按需實(shí)例化,不想jdk的全部實(shí)例化。
5、dubbo的spi有ioc的功能。
二、dubbo spi的三種
1.普通的獲取擴(kuò)展類。
Protocol dubbo = ExtensionLoader.getExtensionLoader(Protocol.class)
.getExtension("dubbo");
@SPI("dubbo")
public interface Protocol
在指定目錄下(dubbo會(huì)掃描3個(gè)目錄)添加指定規(guī)則的文件,描述接口的各個(gè)實(shí)現(xiàn)類
/META_INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
2.自適應(yīng)擴(kuò)展點(diǎn)
通過如下方式獲取自適應(yīng)擴(kuò)展接口實(shí)現(xiàn)類。
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
有兩種:
1)@Adaptive 加在類上。顯示指定這個(gè)類是自適應(yīng)類,通過getAdaptiveExtension方法獲取到的就是這個(gè)類。
2)@Adaptive加在方法上。dubbo會(huì)產(chǎn)生一個(gè)代理類(默認(rèn)是javasisit動(dòng)態(tài)編碼產(chǎn)生)。getAdaptiveExtension方法獲取到的就是這個(gè)代理類。代理類會(huì)根據(jù)傳參,通過URL參數(shù)動(dòng)態(tài)決定調(diào)用哪一個(gè)實(shí)現(xiàn)類的這個(gè)方法。
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy();
}
3.自動(dòng)激活擴(kuò)展點(diǎn)
能夠根據(jù)傳參,來指定具體激活獲取到哪些實(shí)現(xiàn)類。
@Activate(group = {CONSUMER, PROVIDER}, value = CACHE_KEY)
public class CacheFilter implements Filter
自動(dòng)激活擴(kuò)展點(diǎn)可以根據(jù)url參數(shù),指定加載激活哪些實(shí)現(xiàn)類。
activate有兩個(gè)條件指定,1.group??梢允莄onsumer、provider。2.value。
調(diào)用方式:
ExtensionLoader<Filter> loader = ExtensionLoader.getExtensionLoader(Filter.class);
/*
URL url = new URL("", "", 0);
url = url.addParameter("cache", "123");
// 參數(shù)url中有cache這個(gè)key,這里就能獲取到CacheFilter
List<Filter> filters = loader.getActivateExtension(url, "zhangsan");
*/
URL url = new URL("", "", 0);
url = url.addParameter("ab", "cache");
// 傳參ab,會(huì)根據(jù)這個(gè)參數(shù),從url中獲取具體的值,得到cache,所以這里也能獲取到cacheFilter
List<Filter> filters = loader.getActivateExtension(url, "ab");
System.out.println(filters);
System.out.println(filters.size());
4.題外話
javasisit只是編譯和加載,不能重新加載類,所以不能達(dá)到:
@ApiOperator("name")
@NotBlank("name不能魏空")
@Length(max = 20, value = "name不能超過20")
替換為
@MyNotBlankLengthApi("name", max = 40)
的效果。
從ExtensionLoader的源碼中可以看出:
每個(gè)方法開頭都會(huì)對參數(shù)進(jìn)行合法性校驗(yàn),而我實(shí)際開發(fā)中,為了代碼簡潔,減少if、else,往往會(huì)約定大于配置的那種思想,默認(rèn)這個(gè)參數(shù)就是合法的,因?yàn)槲矣X得,代碼都是自己寫的,都是內(nèi)部調(diào)用,代碼文檔也會(huì)說清楚,如果為了參數(shù)校驗(yàn),增加很多if、else顯得多余。但是dubbo是通用工具類,所以要做好這部分的配置。
(寫出來的目的是,希望隔了很久之后,看了筆記還能回想起來)