摘要:在中配置,以配置為例整個,最先使用的地方從里面讀取這個配置使用接口的中獲取具體的實現類中有兩個值當主線程被外部終止時,會觸發,執行的與方法通知下面的鎖操作,主線程正常走完代碼,并最終停止。
spring是如何啟動容器的
常見的一種在本地使用main方法啟動spring的方法
public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"}); context.start(); ... //System.in.read(); // 按任意鍵退出 context.close(); }dubbo是如何啟動容器的
這個大家應該都知道,通過com.alibaba.dubbo.container.Main.main方法來啟動的。
public class Main { //在dubbo.properties中配置, 以配置dubbo.container=log4j,spring為例 public static final String CONTAINER_KEY = "dubbo.container"; public static final String SHUTDOWN_HOOK_KEY = "dubbo.shutdown.hook"; private static final Logger logger = LoggerFactory.getLogger(Main.class); //整個dubbo,最先使用ExtensionLoader的地方 private static final ExtensionLoaderdubbo容器的SPI功能實現loader = ExtensionLoader.getExtensionLoader(Container.class); private static volatile boolean running = true; public static void main(String[] args) { try { //1. 從dubbo.properties里面讀取dubbo.container這個配置; if (args == null || args.length == 0) { String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName()); args = Constants.COMMA_SPLIT_PATTERN.split(config); } //2. 使用Container接口的ExtensionLoader中獲取具體的Container實現類; final List containers = new ArrayList (); //agrs中有兩個值 "log4j,spring" for (int i = 0; i < args.length; i++) { containers.add(loader.getExtension(args[i])); } logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce."); if ("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY))) { //5. 當主線程被外部終止時,會觸發 shutdownhook,執行Container的stop與close方法 Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { for (Container container : containers) { try { container.stop(); logger.info("Dubbo " + container.getClass().getSimpleName() + " stopped!"); } catch (Throwable t) { logger.error(t.getMessage(), t); } synchronized (Main.class) { running = false; //6.通知下面的鎖操作,主線程正常走完代碼,并最終停止。 Main.class.notify(); } } } }); } //3. 執行Container接口的start方法; for (Container container : containers) { container.start(); logger.info("Dubbo " + container.getClass().getSimpleName() + " started!"); } System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " Dubbo service server started!"); } catch (RuntimeException e) { e.printStackTrace(); logger.error(e.getMessage(), e); System.exit(1); } //4. 用一個死循環,保留主線程; synchronized (Main.class) { while (running) { try { Main.class.wait(); } catch (Throwable e) { } } } } }
明確下面幾個概念
擴展接口 com.alibaba.dubbo.container.Container
擴展配置 dubbo.container = log4j,spring
擴展實現
com.alibaba.dubbo.container.log4j.Log4jContainer
log4j的日志初始工作,當多進程啟動時,做日志隔離
com.alibaba.dubbo.container.logback.LogbackContainer
logback的日志初始工作
com.alibaba.dubbo.container.spring.SpringContainer
spring容器的啟動,使用spring容器來實現aop與ioc,**【這個配置,往往是必選的】**
com.alibaba.dubbo.container.jetty.JettyContainer
啟動一個Servlet Web容器,提供了一個web頁面,做一些監控之類的時期,注意:在寫HttpResponse的時候,也是用SPI機制,不同的請
求頁面經過PageServlet交個不同的PageHandler去實現
com.alibaba.dubbo.monitor.simple.RegistryContainer
我們來想一個這樣的問題,上面是dubbo支持的容器,包括log4j、logback、spring、jetty、registry,那么dubbo是如何通過配置的方式來實現容器的可擴展的呢?假如給你做你怎么做呢?
spring的API(Application Programming Interface、應用編程接口)方式,接口多實現類的動態調動;
JDK標準的SPI(Service Provider Interface、)機制
dubbo的擴展點加載機制是從JDK的spi機制加強而來。
dubbo改進了JDK標準的SPI機制以下問題:
spring與JDK的SPI都會一次性實例化擴展點所有實現,如果有擴展實現初始化很耗時,但如果沒用上,也會加載。
JDK的SPI機制不支持Ioc與Aop功能,而dubbo中的擴展點可以直接setter注入其他擴展點。【這個一部分,下面會有涉及,我們會在下一個文章中詳細描述】
擴展接口Container源碼關鍵說明,
必須帶有SPI注解
注解里面的值,是默認實現,在ExtensionLoader源碼去細講。
/** * Container. (SPI, Singleton, ThreadSafe) * * @author william.liangf */ @SPI("spring") public interface Container { /** * start. */ void start(); /** * stop. */ void stop(); }ExtensionLoader源碼
關鍵說明,
1. ExtensionLoader有一個private的構造函數,并通過getExtensionLoader這個鏡頭方法返回實例,是一個單例工廠類。 2. 一個擴展接口對應一個ExtensionLoader實例,也就是說最終我們加載了多少個擴展接口(注意是擴展接口,而不是擴展實現類),就多少個實例; 3. 關鍵static final變量,所有實例共享
private static final ConcurrentMap, ExtensionLoader>> EXTENSION_LOADERS = new ConcurrentHashMap , ExtensionLoader>>(); private static final ConcurrentMap , Object> EXTENSION_INSTANCES = new ConcurrentHashMap , Object>();
4. 所有的final變量,單個實例共享,每一個擴展接口對應的ExtensionLoader都不一樣
//擴展接口名稱 private final Class> type; //也是一個擴展接口,用于注入擴展接口中需要注入的類,實現dubbo的擴展點的自動注入 private final ExtensionFactory objectFactory; private final ConcurrentMap, String> cachedNames = new ConcurrentHashMap , String>(); private final Holder
結合Main類的使用,講一下幾個核心方法
核心方法 -> ExtensionLoader.getExtensionLoader獲得ExtensionLoader實例
private static final ExtensionLoaderloader = ExtensionLoader.**getExtensionLoader**(Container.class);
獲取ExtensionLoader實例
getExtensionLoader(Container.class)【將返回的實例放到EXTENSION_LOADERS變量中】
new ExtensionLoader
獲取ExtensionLoader實例結束
核心方法 -> ExtensionLoader.getExtension獲得擴展實現
注意此時已經拿到了擴展接口Container對應的那個ExtensionLoader實例了,在下面的處理中,基本都是更新這個實例的變量,而很少會更新類變量了。
for (int i = 0; i < args.length; i++) { containers.add(loader.getExtension(args[i])); }
getExtension("log4j" or "spring" or "logback" ....)
createExtension("log4j" or "spring" or "logback" ....) --創建指定類型的擴展接口的instance
getExtensionClasses() --加載擴展接口的所有class文件
loadExtensionClasses() --擴展接口的所有的class文件
loadFile() --從三個路徑下,查找class文件
clazz.newInstance() --創建指定class的instance
injectExtension(instace) --注入屬性Ioc
objectFactory.getExtension(pt, property) --反射的方式,解析setXxx(Xxx xxx)方法,注入Xxx實例
injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); --對實例進行層層包裝,最終返回一個包裝過后的instance
上面總體邏輯就是
圖片
具體介紹一下loadFile方法
//... 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/"; //... Map> extensionClasses = new HashMap >(); loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY); loadFile(extensionClasses, DUBBO_DIRECTORY); loadFile(extensionClasses, SERVICES_DIRECTORY); return extensionClasses;
if (clazz.isAnnotationPresent(Adaptive.class)) { if (cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } else if (!cachedAdaptiveClass.equals(clazz)) { throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName()); } } else { try { clazz.getConstructor(type); Set> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet >(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } catch (NoSuchMethodException e) { clazz.getConstructor(); if (name == null || name.length() == 0) { name = findAnnotationName(clazz); if (name == null || name.length() == 0) { if (clazz.getSimpleName().length() > type.getSimpleName().length() && clazz.getSimpleName().endsWith(type.getSimpleName())) { name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase(); } else { throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url); } } } String[] names = NAME_SEPARATOR.split(name); if (names != null && names.length > 0) { Activate activate = clazz.getAnnotation(Activate.class); if (activate != null) { cachedActivates.put(names[0], activate); } for (String n : names) { if (!cachedNames.containsKey(clazz)) { cachedNames.put(clazz, n); } Class> c = extensionClasses.get(n); if (c == null) { extensionClasses.put(n, clazz); } else if (c != clazz) { throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName()); } } } } }
從上面三個路徑下加載dubbo擴展點的配置。我們以DUBBO_INTERNAL_DIRECTORY路徑下的配置文件為例,說明下dubbo下擴展的配置。
擴展接口實現類,實現Container接口,例如SpringContainer.java
在資源META-INF.dubbo.internal文件夾下,有一個以Container接口全路徑名稱為名字的文件;
上述文件名中內容格式為 {key}={value},key為擴展點實現類的配置名稱,例如spring、log4j等;value為SpringContainer類的全路徑名稱
loadFile中就是以這樣的規則,解析這樣的配置文件,并放到extensionClasses這樣的Map中返回,extensionClasses的key是這個{key},value是這個{value}對應的class。
這里面主要是四個邏輯,涉及到幾種情況。
圖片
for (Container container : containers) { container.start(); logger.info("Dubbo " + container.getClass().getSimpleName() + " started!"); }
執行SpringContainer.java的start方法
public void start() { String configPath = ConfigUtils.getProperty(SPRING_CONFIG); if (configPath == null || configPath.length() == 0) { configPath = DEFAULT_SPRING_CONFIG; } context = new ClassPathXmlApplicationContext(configPath.split("[,s]+")); context.start(); }
這個不是這篇文章最開始的那個問題的答案嘛,原來dubbo就是通過這么簡單的方式的來啟動spring容器的。這算是一個首尾呼應嘛~ 終于,終于,第一篇文章寫完了~ 下篇文章會講解擴展點是如何IOC的。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69058.html
摘要:今天我想聊聊的另一個很棒的特性就是它的可擴展性。的擴展機制在的官網上,描述自己是一個高性能的框架。接下來的章節中我們會慢慢揭開擴展機制的神秘面紗。擴展擴展點的實現類。的定義在配置文件中可以看到文件中定義了個的擴展實現。 摘要: 在Dubbo的官網上,Dubbo描述自己是一個高性能的RPC框架。今天我想聊聊Dubbo的另一個很棒的特性, 就是它的可擴展性。 Dubbo的擴展機制 在Dub...
摘要:什么是類那什么樣類的才是擴展機制中的類呢類是一個有復制構造函數的類,也是典型的裝飾者模式。代碼如下有一個參數是的復制構造函數有一個構造函數,參數是擴展點,所以它是一個擴展機制中的類。 摘要:?在Dubbo可擴展機制實戰中,我們了解了Dubbo擴展機制的一些概念,初探了Dubbo中LoadBalance的實現,并自己實現了一個LoadBalance。是不是覺得Dubbo的擴展機制很不錯呀...
摘要:二注解該注解為了保證在內部調用具體實現的時候不是硬編碼來指定引用哪個實現,也就是為了適配一個接口的多種實現,這樣做符合模塊接口設計的可插拔原則,也增加了整個框架的靈活性,該注解也實現了擴展點自動裝配的特性。 Dubbo擴展機制SPI 前一篇文章《dubbo源碼解析(一)Hello,Dubbo》是對dubbo整個項目大體的介紹,而從這篇文章開始,我將會從源碼來解讀dubbo再各個模塊的實...
摘要:要構建自適應實例,先要有自適應的實現類,實現類有兩種方式一種通過配置文件,一種是通過是字節碼的方式動態生成。 SPI機制 SPI,即(service provider interface)機制,有很多組件的實現,如日志、數據庫訪問等都是采用這樣的方式,一般通用組件為了提升可擴展性,基于接口編程,將操作接口形成標準規范,但是可以開放多種擴展實現,這種做法也符合開閉設計原則,使組件具有可插...
摘要:為了實現在模塊裝配的時候,不在模塊里寫死代碼,就需要一種服務發現機制。就提供了這樣一種機制為某個接口尋找服務實現,有點類似思想,將裝配的控制權移到代碼之外。即接口文件的全類名。五示例遵循上述第一條第點,這里為接口文件,其中和為兩個實現類。 一、Dubbo內核 Dubbo內核主要包含SPI、AOP、IOC、Compiler。 二、JDK的SPI 1.spi的設計目標: 面向對象的設計里...
閱讀 3195·2023-04-26 01:39
閱讀 3349·2023-04-25 18:09
閱讀 1617·2021-10-08 10:05
閱讀 3233·2021-09-22 15:45
閱讀 2775·2019-08-30 15:55
閱讀 2397·2019-08-30 15:54
閱讀 3170·2019-08-30 15:53
閱讀 1328·2019-08-29 12:32