以前寫了一篇關於dubbo spi的文章,如今看來後面的代碼交代的不清楚,今天作一點補充。java
http://my.oschina.net/zjItLife/blog/530923編程
dubbo的spi重要的是理解類ExtensionLoader。緩存
這個類的頭部做者就交代了這個類要作什麼app
Dubbo使用的擴展點獲取。this
自動注入關聯擴展點。.net
自動Wrap上擴展點的Wrap類。code
缺省得到的的擴展點是一個Adaptive Instance。blog
那麼到底是怎麼作的咱們不妨來看看他的變量在作什麼。接口
dubbo變量裏面有兩個表示文件夾的變量get
private static final String SERVICES_DIRECTORY = "META-INF/services/"; private static final String DUBBO_DIRECTORY = "META-INF/dubbo/"; private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
下文涉及到這三個變量的方法在這裏。
LoadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY); loadFile(extensionClasses, DUBBO_DIRECTORY); LoadFile(extensionClasses, SERVICES_DIRECTORY);
也就是說擴展類extensionClasses都是從這三個文件夾裏面獲取的。 接下來的兩個變量
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>(); private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
下文的方法都是從這兩個容器得到ExtensionLoader類和ExtensionLoader類實例
接下來是兩個變量
private final Class<?> type; private final ExtensionFactory objectFactory;
一個是該ExtensionLoader類表明的接口類型還有就是生成ExtensionLoader的適配器工廠。
接下來是緩存容器
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>(); private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String,Class<?>>>(); private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>(); private volatile Class<?> cachedAdaptiveClass = null; private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>(); private String cachedDefaultName; private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>(); private volatile Throwable createAdaptiveInstanceError; private Set<Class<?>> cachedWrapperClasses;
還有一個容錯容器
private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();
接下來咱們看看各個方法都作了什麼 一、何時生成了這個ExtensionLoader
dubbo代碼中不少地方都調用了ExtensionLoader類的方法getExtensionLoader。該方法採用單利模式生成ExtensionLoader類的實例。
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { //首先判斷出入的類是否爲爲空和接口 if (type == null) throw new IllegalArgumentException("Extension type == null"); if(!type.isInterface()) { throw new IllegalArgumentException("Extension type(" + type + ") is not interface!"); } //判斷是否打了註解@SPI if(!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); } //先從容器中獲取 ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) { //獲取不到生成一個並放入容器。 EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader; } private ExtensionLoader(Class<?> type) { //生成的時候將類型傳入,並生成一個類型爲ExtensionFactory的適配器,賦值給objectFactory。 this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
二、基於類型的適配器類和擴展類是如何被掃描進來的?
在交代變量的時候咱們發現了掃描上文件夾下的類集合,那麼這些獲取擴展類的方法在方法loadExtensionClasses中
// 此方法已經getExtensionClasses方法同步過。 private Map<String, Class<?>> loadExtensionClasses() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if(defaultAnnotation != null) { //獲取@SPI裏面的值,這個值後來用來獲取默認的實現類。 String value = defaultAnnotation.value(); if(value != null && (value = value.trim()).length() > 0) { String[] names = NAME_SEPARATOR.split(value); if(names.length > 1) { throw new IllegalStateException("more than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names)); } if(names.length == 1) cachedDefaultName = names[0]; } } Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY); loadFile(extensionClasses, DUBBO_DIRECTORY); loadFile(extensionClasses, SERVICES_DIRECTORY); return extensionClasses; }
這個類的做用只是將三個文件夾裏面與type一致的名稱和類的映射關係維護到類容器中,咱們仔細看看這個類。
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) { //獲取文件路徑,由於dubbo的各個配置文件符合文件夾+接口名稱這樣的規則 //1獲取類加載器 //二、根據類加載器加載文件 //三、遍歷文件 //四、將每一行信息按照「=」分隔。 //4.一、獲取=右面的類。 //4.二、保證獲取的類是不是type接口的實現類。 //4.三、若是類標有@Adaptive,則將這個類放置到適配器類的位置。(適配器類只能是一個) //4.四、若是不是適配器類,則將其其名稱和類之間的關係維護到容器cachedNames和extensionClasses中 }
三、dubbo spi重要的就是在初期生成了一個適配器類,用作以後找到真正實現類的中轉站。
injectExtension((T) getAdaptiveExtensionClass().newInstance());
一、生成適配器類
二、實例化適配器類
三、若是是工廠類的適配器則injectExtension未作任何事情。若是非工廠類會獲取全部set方法的屬性值,若是工廠類可以查找到相應的適配器類,則將其注入進來。
四、 尋找擴展點,dubbo spi費了半天的勁兒去實例化了一個適配器,做用仍是在生成擴展點起做用。
4.一、獲取缺省的擴展點
上文已經交代,在生成ExtensionLoader實例的時候就找到了一個默認的擴展點名稱,將其放置到了變量cachedDefaultName中。根據這個名稱,咱們能夠生成默認的擴展點實例。
public T getDefaultExtension() { getExtensionClasses(); if(null == cachedDefaultName || cachedDefaultName.length() == 0 || "true".equals(cachedDefaultName)) { return null; } return getExtension(cachedDefaultName); }
4.二、獲取指定名稱的擴展點
T getExtension(String name)
4.三、獲取指定名稱的擴展類
private Class<?> getExtensionClass(String name)
五、編程方式注入擴展點
5.一、編程方式添加新擴展點
public void addExtension(String name, Class<?> clazz)
5.二、編程方式添加替換已有擴展點
public void replaceExtension(String name, Class<?> clazz)
至此,ExtensionLoader類就交代完了,對於這個類的理解將對咱們深刻理解dubbo源碼有必定幫助。
做者hyssop,請君留言批評指正。