motan源碼分析二:使用spi機制進行類載入
在motan的源碼中使用了很多的spi機制進行對象的建立,下面我們來具體分析一下它的實現方法。
1.在實際的jar包的\META-INF\services目錄中引入相關的檔案,例如下圖中,我解壓了core的jar檔案後,獲得到的相應檔案清單:
2.以第一節中的ConfigHandler為例來分析,開啟上圖中的com.weibo.api.motan.config.handler.ConfigHandler檔案,檔案內容標識著ConfigHandler介面的實作類別為:com.weibo.api.motan.config.handler.SimpleConfigHandler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # # Copyright 2009 - 2016 Weibo, Inc. # # Licensed under the Apache License, Version 2.0 (the "License" ); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http: //www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # com.weibo.api.motan.config.handler.SimpleConfigHandler
3.在第一節中,建立ConfigHandler對象的代碼是這樣的:
ConfigHandler configHandler = ExtensionLoader.getExtensionLoader(ConfigHandler.class).getExtension(MotanConstants.DEFAULT_VALUE);
4.開始進入到實際的載入代碼核心部分,首先來看一下類載入器的具體實現:
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { checkInterfaceType(type);//基礎性檢查 ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoaders.get(type);//之前是否已經載入過此載入器 if (loader == null) { loader = initExtensionLoader(type);//第一次載入 } return loader; } private static <T> void checkInterfaceType(Class<T> clz) { if (clz == null) { failThrows(clz, "Error extension type is null"); } if (!clz.isInterface()) { failThrows(clz, "Error extension type is not interface"); } if (!isSpiType(clz)) { failThrows(clz, "Error extension type without @Spi annotation"); } } public static synchronized <T> ExtensionLoader<T> initExtensionLoader(Class<T> type) { ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoaders.get(type); if (loader == null) { loader = new ExtensionLoader<T>(type);//新建立一個載入器 extensionLoaders.putIfAbsent(type, loader); loader = (ExtensionLoader<T>) extensionLoaders.get(type); } return loader; }
5.下面我們將進入到載入器的內部,分析具體的實現:
private ExtensionLoader(Class<T> type) { this(type, Thread.currentThread().getContextClassLoader());//使用當前線程的類載入器做為載入器,type為ConfigHandler介面 } public T getExtension(String name) { checkInit();//檢查是否初始化 if (name == null) { return null; } try { Spi spi = type.getAnnotation(Spi.class); if (spi.scope() == Scope.SINGLETON) { return getSingletonInstance(name);//返回唯一的對象 } else { Class<T> clz = extensionClasses.get(name); if (clz == null) { return null; } return clz.newInstance();//重新建立對象 } } catch (Exception e) { failThrows(type, "Error when getExtension " + name, e); } return null; } private synchronized void loadExtensionClasses() { if (init) { return; } extensionClasses = loadExtensionClasses(PREFIX);//載入相關的類 singletonInstances = new ConcurrentHashMap<String, T>(); init = true; } private ConcurrentMap<String, Class<T>> loadExtensionClasses(String prefix) { String fullName = prefix + type.getName();//全名為:jar包名+\META-INF\services\com.weibo.api.motan.config.handler.ConfigHandler檔案裡的類 List<String> classNames = new ArrayList<String>(); try { Enumeration<URL> urls; if (classLoader == null) { urls = ClassLoader.getSystemResources(fullName); } else { urls = classLoader.getResources(fullName); } if (urls == null || !urls.hasMoreElements()) { return new ConcurrentHashMap<String, Class<T>>(); } System.out.println("fullname:"+fullName); while (urls.hasMoreElements()) { URL url = urls.nextElement(); System.out.println("url:"+url.getFile()); parseUrl(type, url, classNames); } } catch (Exception e) { throw new MotanFrameworkException( "ExtensionLoader loadExtensionClasses error, prefix: " + prefix + " type: " + type.getClass(), e); } for(String classN : classNames){ System.out.println("class:"+classN); } return loadClass(classNames); }
6.在parseUrl方法中進行檔案的內容讀取,並在loadClass中完成類的載入
private void parseUrl(Class<T> type, URL url, List<String> classNames) throws ServiceConfigurationError { InputStream inputStream = null; BufferedReader reader = null; try { inputStream = url.openStream(); reader = new BufferedReader(new InputStreamReader(inputStream, MotanConstants.DEFAULT_CHARACTER)); String line = null; int indexNumber = 0; while ((line = reader.readLine()) != null) { indexNumber++; parseLine(type, url, line, indexNumber, classNames);//讀取到類的名稱:com.weibo.api.motan.config.handler.SimpleConfigHandler } } catch (Exception x) { failLog(type, "Error reading spi configuration file", x); } finally { try { if (reader != null) { reader.close(); } if (inputStream != null) { inputStream.close(); } } catch (IOException y) { failLog(type, "Error closing spi configuration file", y); } } } private ConcurrentMap<String, Class<T>> loadClass(List<String> classNames) { ConcurrentMap<String, Class<T>> map = new ConcurrentHashMap<String, Class<T>>(); for (String className : classNames) { try { Class<T> clz; if (classLoader == null) { clz = (Class<T>) Class.forName(className);//裝載類:com.weibo.api.motan.config.handler.SimpleConfigHandler } else { clz = (Class<T>) Class.forName(className, true, classLoader); } checkExtensionType(clz); String spiName = getSpiName(clz); if (map.containsKey(spiName)) { failThrows(clz, ":Error spiName already exist " + spiName); } else { map.put(spiName, clz); } } catch (Exception e) { failLog(type, "Error load spi class", e); } } return map; }
motan類載入的知識點總結:
1.使用jdk的spi規範,在\META-INF\services中添加實際的使用類描述,從而實作類別與類之間的完全解耦;
2.類載入器使用的是當前線程的類載入器;
3.motan的類載入器可以支援單例和多例兩種模式;
4.motan中大量使用了spi的類載入方式。