常见的一种在本地使用main方法启动spring的方法java
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(); }
这个你们应该都知道,经过com.alibaba.dubbo.container.Main.main方法来启动的。web
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 ExtensionLoader<Container> 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<Container> containers = new ArrayList<Container>(); //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) { } } } } }
明确下面几个概念spring
com.alibaba.dubbo.container.log4j.Log4jContainer编程
log4j的日志初始工做,当多进程启动时,作日志隔离
com.alibaba.dubbo.container.logback.LogbackContainerapp
logback的日志初始工做
com.alibaba.dubbo.container.spring.SpringContaineride
spring容器的启动,使用spring容器来实现aop与ioc,**【这个配置,每每是必选的】**
com.alibaba.dubbo.container.jetty.JettyContainer函数
启动一个Servlet Web容器,提供了一个web页面,作一些监控之类的时期,注意:在写HttpResponse的时候,也是用SPI机制,不一样的请
求页面通过PageServlet交个不一样的PageHandler去实现url
咱们来想一个这样的问题,上面是dubbo支持的容器,包括log4j、logback、spring、jetty、registry,那么dubbo是如何经过配置的方式来实现容器的可扩展的呢?假如给你作你怎么作呢?线程
关键说明,日志
/** * Container. (SPI, Singleton, ThreadSafe) * * @author william.liangf */ @SPI("spring") public interface Container { /** * start. */ void start(); /** * stop. */ void stop(); }
关键说明,
1. ExtensionLoader有一个private的构造函数,并经过getExtensionLoader这个镜头方法返回实例,是一个单例工厂类。 2. 一个扩展接口对应一个ExtensionLoader实例,也就是说最终咱们加载了多少个扩展接口(注意是扩展接口,而不是扩展实现类),就多少个实例; 3. 关键static final变量,全部实例共享
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>(); private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
4. 全部的final变量,单个实例共享,每个扩展接口对应的ExtensionLoader都不同
//扩展接口名称 private final Class<?> type; //也是一个扩展接口,用于注入扩展接口中须要注入的类,实现dubbo的扩展点的自动注入 private final ExtensionFactory objectFactory; 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 final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>(); private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>(); private volatile Class<?> cachedAdaptiveClass = null; private String cachedDefaultName; private volatile Throwable createAdaptiveInstanceError; private Set<Class<?>> cachedWrapperClasses; private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();
结合Main类的使用,讲一下几个核心方法
得到ExtensionLoader实例
private static final ExtensionLoader<Container> loader = ExtensionLoader.**getExtensionLoader**(Container.class);
获取ExtensionLoader实例
getExtensionLoader(Container.class)【将返回的实例放到EXTENSION_LOADERS变量中】
获取ExtensionLoader实例结束
得到扩展实现
注意此时已经拿到了扩展接口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文件
injectExtension(instace) --注入属性Ioc
上面整体逻辑就是
图片
具体介绍一下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<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); 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<Class<?>> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); 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下扩展的配置。
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的。