Dubbo 採用全 Spring 配置方式,官方推薦使用內置 Main 啓動,並提供了JDK 的 ShutdownHook 優雅停機。這裏看的是dubbo 2.6.2版本的代碼java
貼出dubbo提供的Main啓動類spring
1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package com.alibaba.dubbo.container; 18 19 import com.alibaba.dubbo.common.Constants; 20 import com.alibaba.dubbo.common.extension.ExtensionLoader; 21 import com.alibaba.dubbo.common.logger.Logger; 22 import com.alibaba.dubbo.common.logger.LoggerFactory; 23 import com.alibaba.dubbo.common.utils.ConfigUtils; 24 25 import java.text.SimpleDateFormat; 26 import java.util.ArrayList; 27 import java.util.Arrays; 28 import java.util.Date; 29 import java.util.List; 30 import java.util.concurrent.locks.Condition; 31 import java.util.concurrent.locks.ReentrantLock; 32 33 /** 34 * Main. (API, Static, ThreadSafe) 35 */ 36 public class Main { 37 38 public static final String CONTAINER_KEY = "dubbo.container"; 39 40 public static final String SHUTDOWN_HOOK_KEY = "dubbo.shutdown.hook"; 41 42 private static final Logger logger = LoggerFactory.getLogger(Main.class); 43 44 private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class); 45 46 private static final ReentrantLock LOCK = new ReentrantLock(); 47 48 private static final Condition STOP = LOCK.newCondition(); 49 50 public static void main(String[] args) { 51 try { 52 if (args == null || args.length == 0) { 53 String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName()); 54 args = Constants.COMMA_SPLIT_PATTERN.split(config); 55 } 56 57 final List<Container> containers = new ArrayList<Container>(); 58 for (int i = 0; i < args.length; i++) { 59 containers.add(loader.getExtension(args[i])); 60 } 61 logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce."); 62 63 if ("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY))) { 64 Runtime.getRuntime().addShutdownHook(new Thread() { 65 @Override 66 public void run() { 67 for (Container container : containers) { 68 try { 69 container.stop(); 70 logger.info("Dubbo " + container.getClass().getSimpleName() + " stopped!"); 71 } catch (Throwable t) { 72 logger.error(t.getMessage(), t); 73 } 74 try { 75 LOCK.lock(); 76 STOP.signal(); 77 } finally { 78 LOCK.unlock(); 79 } 80 } 81 } 82 }); 83 } 84 85 for (Container container : containers) { 86 container.start(); 87 logger.info("Dubbo " + container.getClass().getSimpleName() + " started!"); 88 } 89 System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " Dubbo service server started!"); 90 } catch (RuntimeException e) { 91 e.printStackTrace(); 92 logger.error(e.getMessage(), e); 93 System.exit(1); 94 } 95 try { 96 LOCK.lock(); 97 STOP.await(); 98 } catch (InterruptedException e) { 99 logger.warn("Dubbo service server stopped, interrupted by other thread!", e); 100 } finally { 101 LOCK.unlock(); 102 } 103 } 104 105 }
分析main方法 main方法的參數傳入的是要啓動的容器類(Container),dubbo提供了三個實現,分別是SpringContainer、Log4jContainer、LogbackContainer,並在這裏配置好express
分析 apache
首先判斷是否傳入了啓動容器,若是沒有傳入則從-D參數數或者dubbo.properties文件中獲取key dubbo.container的值,若是都沒獲取到那麼就默認使用默認容器啓動,默認容器是哪一個呢? 查看 getDefaultExtensionName 方法能夠看到調用的loadExtensionClasses()方法中有獲取默認容器的代碼 以下 app
1 // synchronized in getExtensionClasses 2 private Map<String, Class<?>> loadExtensionClasses() { 3 final SPI defaultAnnotation = type.getAnnotation(SPI.class); 4 if (defaultAnnotation != null) { 5 String value = defaultAnnotation.value(); 6 if ((value = value.trim()).length() > 0) { 7 String[] names = NAME_SEPARATOR.split(value); 8 if (names.length > 1) { 9 throw new IllegalStateException("more than 1 default extension name on extension " + type.getName() 10 + ": " + Arrays.toString(names)); 11 } 12 if (names.length == 1) cachedDefaultName = names[0]; 13 } 14 } 15 16 Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); 17 loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY); 18 loadDirectory(extensionClasses, DUBBO_DIRECTORY); 19 loadDirectory(extensionClasses, SERVICES_DIRECTORY); 20 return extensionClasses; 21 }
在這裏獲取了Container接口上的註解SPI的value,回去看Container接口 能夠看到接口上有@SPI("spring")註解,value就是spring 因此這裏會默認使用spring容器啓動less
而後繼續往下看ide
會循環獲取到的啓動類列表獲取啓動類實例,就是在META-INF/dubbo.internal/{接口名稱} 對應的文件會有鍵值對,就是對應的實現類,詳情請看 loader.getExtension 這裏就不展開。ui
繼續往下, 若是能獲取到 dubbo.shutdown.hook 配置而且是true 則加入 addShutdownHook 鉤子 ,這個方法會在程序正常中止時候被調用,dubbo就是使用這個實現的優雅停機,看裏面的代碼,循環拿到啓動的容器類調用stop() 方法 來中止容器,這裏還用到了鎖通知,用來喚醒Main的主程序。this
再往下 是啓動類的啓動方法 , 會循環獲取到的容器實例並調用start()方法,各個實現類的啓動方法能夠查看實現類的實現代碼,這裏貼出SpringContainer的實現spa
1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package com.alibaba.dubbo.container.spring; 18 19 import com.alibaba.dubbo.common.logger.Logger; 20 import com.alibaba.dubbo.common.logger.LoggerFactory; 21 import com.alibaba.dubbo.common.utils.ConfigUtils; 22 import com.alibaba.dubbo.container.Container; 23 24 import org.springframework.context.support.ClassPathXmlApplicationContext; 25 26 /** 27 * SpringContainer. (SPI, Singleton, ThreadSafe) 28 */ 29 public class SpringContainer implements Container { 30 31 public static final String SPRING_CONFIG = "dubbo.spring.config"; 32 public static final String DEFAULT_SPRING_CONFIG = "classpath*:META-INF/spring/*.xml"; 33 private static final Logger logger = LoggerFactory.getLogger(SpringContainer.class); 34 static ClassPathXmlApplicationContext context; 35 36 public static ClassPathXmlApplicationContext getContext() { 37 return context; 38 } 39 40 @Override 41 public void start() { 42 String configPath = ConfigUtils.getProperty(SPRING_CONFIG); 43 if (configPath == null || configPath.length() == 0) { 44 configPath = DEFAULT_SPRING_CONFIG; 45 } 46 context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+")); 47 context.start(); 48 } 49 50 @Override 51 public void stop() { 52 try { 53 if (context != null) { 54 context.stop(); 55 context.close(); 56 context = null; 57 } 58 } catch (Throwable e) { 59 logger.error(e.getMessage(), e); 60 } 61 } 62 63 }
能夠看到start方法初始化了一個spring應用上下文 其實也就是spring容器,這裏若是沒有配置 dubbo.spring.config 則會默認掃描 classpath*:META-INF/spring/*.xml 文件來加載spring bean.
stop方法關閉spring容器。
main方法的最後使用了Lock和Condition 而且使主線程等待,其實這裏應該就是起到阻塞主線程的做用。關閉的鉤子裏會喚醒使主線程結束。
over...