龙哥dubbo源码阅读实践-源码入口(第一章)

资源准备

1.dubbo开发手册下载:
地址:http://dubbo.io/docs/dubbo-dev-book.pdf
2.dubbo源码下载
地址:https://github.com/alibaba/dubbo/tags
3.导入项目到eclipse或者idea
注意:
- dubbo项目是标准的maven工程,直接以maven项目导入即可
- setting配置可以不配置,默认会从repo.maven.org拉去依赖
4.构建源代码
mvn package -Dmaven.test.skip=true

源码阅读

启动方式(dubbo项目一般有两种启动方式)

1.Standlone模式,通过Main加载Spring启动
2.使用容器tomcat、jetty、resin等加载Spring启动
两种启动方式,最终都和spring启动过程紧密相关,其实dubbo就是利用spring的扩展性,将自己的启动流程无缝的融合到spring的启动过程中。

启动流程

先看一张完整的启动时序图,如下图:
这里写图片描述
下载此图

这种大图,看起来会相当苦恼,但是细心的你,如果能坚持看完,那么对于dubbo启动的流程,应该已经心中有数。如果有点懵,没关系,图中我省略了一些模块,并且还有些是父子类方法的相互调用,下面我会用相当的篇幅来解释这张图。

从Main函数到SpringContainer

在dubbo源码的dubbo-container-api模块的src/main/resources/assembly/bin目录下,存放了一些脚本,这些脚本是预留给使用dubbo项目的开发者们启动项目使用的,其中有6个shell脚本:

dump.sh 通过jstack,jinfo,jmap等命令dump内存信息
restart.sh 重启项目
server.sh 集成其他几个命令
start.bat windows上启动项目
start.sh Linux上启动项目
stop.sh 优雅关闭

我们打开start.sh这个脚本,会看到如下的命令:

nohup java $JAVA_OPTS</span> <span class="hljs-variable">$JAVA_MEM_OPTS $JAVA_DEBUG_OPTS</span> <span class="hljs-variable">$JAVA_JMX_OPTS -classpath $CONF_DIR</span>:<span class="hljs-variable">$LIB_JARS com.alibaba.dubbo.container.Main > $STDOUT_FILE 2>&1 &
   
   
  • 1

这是通过java命令来调用com.alibaba.dubbo.container.Main类中的入口函数main,看看代码

public static void main(String[] args) {
        try {
            if (args == null || args.length == 0) {
                String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
                args = Constants.COMMA_SPLIT_PATTERN.split(config);
            }

            final List<Container> containers = new ArrayList<Container>();
            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))) {
                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);
                            }
                            try {
                                LOCK.lock();
                                STOP.signal();
                            } finally {
                                LOCK.unlock();
                            }
                        }
                    }
                });
            }

            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);
        }
        try {
            LOCK.lock();
            STOP.await();
        } catch (InterruptedException e) {
            logger.warn("Dubbo service server stopped, interrupted by other thread!", e);
        } finally {
            LOCK.unlock();
        }
    }

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

重点在String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName()); 这一行代码上
这个loader是一个成员变量,定义如下:
private static final ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Container.class);传入的泛型类是Container,该类的定义为:

@SPI("spring")
public interface Container {

    /**
     * start.
     */
    void start();

    /**
     * stop.
     */
    void stop();

}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

我们跟loader.getDefaultExtensionName()方法走下去,会进到ExtensionLoader的loadExtensionClasses方法

 private Map<String, Class<?>> loadExtensionClasses() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            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;
    }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

重点在final SPI defaultAnnotation = type.getAnnotation(SPI.class);,type就是泛型类Container,刚刚已经看过这个类,他有@SPI(“spring”)的注释,为此Main类中的main方法的String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());这行代码,返回的是spring,代入原代码中,即是SpringContainer。

SpringContainer

SpringContainer代码很简单

 public static final String SPRING_CONFIG = "dubbo.spring.config";
 public static final String DEFAULT_SPRING_CONFIG = "classpath*:META-INF/spring/*.xml";
 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();
    }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这里我们只要知道这个configPath是从dubbo.properties配置文件中读取的即可,如果我们没有在类路径下配置这个属性,默认是从DEFAULT_SPRING_CONFIG对应的META-INF里去读取的。

Spring初始化流程

在SpringContainer的start方法中,看到了new了一个ClassPathXmlApplicationContext,这个基本上就回到了spring的初始化流程里面,dubbo只是利用了spring支持对schema自定义的扩展类NamespaceHandler和spring对支持xml解析自定义的扩展类EntityResolver,做了一些扩展,从而在spring初始化的过程中顺带着将dubbo给启动起来了。

如果你对spring的启动过程非常清楚,那么可以直接去看DubboNamespaceHandler和PluggableSchemaResolver,如果你还并不是很清楚spring的这一套流程,建议还是耐心的从这里看下去。
在上面new ClassPathXmlApplicationContext(configPath.split(“[,\s]+”))这行代码,实际调用的是该类的三个参数的构建函数:

public ClassPathXmlApplicationContext(String[] configLocations,
            boolean refresh, ApplicationContext parent) throws BeansException {
        super(parent);
        setConfigLocations(configLocations);
        if (refresh)
            refresh();
    }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

refresh实际上调用了是其父类AbstractApplicationContext的方法

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            prepareRefresh();

            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            prepareBeanFactory(beanFactory);
            try {
                postProcessBeanFactory(beanFactory);

                invokeBeanFactoryPostProcessors(beanFactory);

                registerBeanPostProcessors(beanFactory);

                initMessageSource();

                initApplicationEventMulticaster();

                onRefresh();

                registerListeners();

                finishBeanFactoryInitialization(beanFactory);

                finishRefresh();
            } catch (BeansException ex) {
                if (this.logger.isWarnEnabled()) {
                    this.logger
                            .warn(new StringBuilder()
                                    .append("Exception encountered during context initialization - cancelling refresh attempt: ")
                                    .append(ex).toString());
                }

                destroyBeans();

                throw ex;
            } finally {
                resetCommonCaches();
            }
        }
    }

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

这段代码是spring初始化流程的精髓,涵盖了spring的ApplicationContext启动的大部分工作,如果你使用过spring,却从来没有看到过这段代码,那一定是你的损失,强烈的建议你一定要像熟悉回家的路一样,熟悉这段代码,摸清楚每个方法的所做的工作。
针对dubbo初始化,我们关注的方法在obtainFreshBeanFactory里面

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug(new StringBuilder().append("Bean factory for ")
                    .append(getDisplayName()).append(": ").append(beanFactory)
                    .toString());
        }
        return beanFactory;
    }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这个方法里核心调用了refreshBeanFactory和getBeanFactory两个方法,恰巧这两个方法在AbstractApplicationContext里都是abstrast的,

protected abstract void refreshBeanFactory() throws BeansException,
            IllegalStateException;

    protected abstract void closeBeanFactory();

    public abstract ConfigurableListableBeanFactory getBeanFactory()
            throws IllegalStateException;
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这两个抽象方法有两个实现类:
1 AbstractRefreshableApplicationContext
2 GenericApplicationContext
那么具体调用的是哪个类的方法呢?,这个需要我们结合ClassPathXmlApplicationContext的继承关系来看。ClassPathXmlApplicationContext继承了AbstractXmlApplicationContext,AbstractXmlApplicationContext继承了AbstractRefreshableConfigApplicationContext,AbstractRefreshableConfigApplicationContext继承了AbstractRefreshableApplicationContext,咦,就是他。进入到AbstractRefreshableApplicationContext类中,可以看到重点在refreshBeanFactory方法中。

protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        } catch (IOException ex) {
            throw new ApplicationContextException(
                    "I/O error parsing bean definition source for "
                            + getDisplayName(), ex);
        }
    }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

refreshBeanFactory调用了loadBeanDefinitions,这个方法又是abstract的,实现他的类就更多了。
AnnotationConfigWebApplicationContext
GroovyWebApplicationContext
XmlWebApplicationContext
AbstractXmlApplicationContext等等,这里进入的又是哪个类呢?还是按照继承关系的上下文来找,ClassPathXmlApplicationContext继承了AbstractXmlApplicationContext,所以进入的是AbstractXmlApplicationContext的loadBeanDefinitions方法
为了避免篇幅太冗长,非核心代码,我将不再贴出来了,大家看的时候,一定自己结合源码。
loadBeanDefinitions方法进一步调用XmlBeanDefinitionReader类的loadBeanDefinitions方法,该方法会调到该类的doLoadBeanDefinitions方法上,代码如下

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    throws BeanDefinitionStoreException
  {
    try
    {
      Document doc = doLoadDocument(inputSource, resource);
      return registerBeanDefinitions(doc, resource);
    }
    catch (BeanDefinitionStoreException ex) {
      throw ex;
    }
    catch (SAXParseException ex)
    {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex
        .getLineNumber() + " in XML document from " + resource + " is invalid", ex);
    }
   ...
  }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

这个doLoadBeanDefinitions方法太关键了,他的核心代码有两行,这两行做了两个关键的动作
1 doLoadDocument,这个方法用来加载项目中的xml文件,并使用dubbo对应的xsd文件来做校验和检查
2 registerBeanDefinitions,用来解析加载进来的xml文件,针对xml中的每个元素做对应处理,比如spring的要生成一个对应的bean实例放入spring容器中,比如形式的标签要做切面处理,比如标签要生成dubbo模型中的服务端并注册到注册中心等等。
我们知道标签是spring-bean模块的,是spring的aop模块的,甚至还有事务的,的等等,是dubbo自由的,可见spring支持其本身不同模块,还能支持外部的标签,这种强大的扩展性是多么有必要学习啊。 而dubbo的作者正是利用了这个扩展性,无缝的将dubbo和spring对接起来,让使用dubbo的开发者,只要使用过spring,就可以使用dubbo。
针对这两个方法,我会单独抽出来具体的写,我们不仅要学习到dubbo是怎么利用spring的这个特性来实现扩展的,我们更要学会他的本质,对我们今后自己写框架大有帮助。

此处,你大概知道

doLoadDocument

doLoadDocument方法最终会通过spring.schema的内容替换我们项目中配置的spring文件
spring.schema文件中内容

http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd
   
   
  • 1

从而引用到项目本地的xsd文件对spring或者dubbo的配置做校验和检查,最终将这些配置文件加载到内存中,这里有个重要的类DelegatingEntityResolver,他会根据是dtd结尾还是xsd结尾选择不同的Resolver,BeansDtdResolver还是PluggableSchemaResolver。

registerBeanDefinitions

registerBeanDefinitions方法是对doLoadDocument加载进来的文档做解析,生成对应的处理逻辑。他也会解析到spring.handler文件中的内容

http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
   
   
  • 1

DubboNamespaceHandler类的代码

public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
    }

}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

该类的init方法会被调用,从而会在内存中维护一份,当解析到,等等都用DubboBeanDefinitionParser类去处理,
DubboBeanDefinitionParser的parse方法针对不同类型会做不同的处理逻辑

@SuppressWarnings("unchecked")
    private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClass(beanClass);
        beanDefinition.setLazyInit(false);
        String id = element.getAttribute("id");
        if ((id == null || id.length() == 0) && required) {
            String generatedBeanName = element.getAttribute("name");
            if (generatedBeanName == null || generatedBeanName.length() == 0) {
                if (ProtocolConfig.class.equals(beanClass)) {
                    generatedBeanName = "dubbo";
                } else {
                    generatedBeanName = element.getAttribute("interface");
                }
            }
            if (generatedBeanName == null || generatedBeanName.length() == 0) {
                generatedBeanName = beanClass.getName();
            }
            id = generatedBeanName;
            int counter = 2;
            while (parserContext.getRegistry().containsBeanDefinition(id)) {
                id = generatedBeanName + (counter++);
            }
        }
        if (id != null && id.length() > 0) {
            if (parserContext.getRegistry().containsBeanDefinition(id)) {
                throw new IllegalStateException("Duplicate spring bean id " + id);
            }
            parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
            beanDefinition.getPropertyValues().addPropertyValue("id", id);
        }
        if (ProtocolConfig.class.equals(beanClass)) {
            for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {
                BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);
                PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");
                if (property != null) {
                    Object value = property.getValue();
                    if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) {
                        definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
                    }
                }
            }
        } else if (ServiceBean.class.equals(beanClass)) {
            String className = element.getAttribute("class");
            if (className != null && className.length() > 0) {
                RootBeanDefinition classDefinition = new RootBeanDefinition();
                classDefinition.setBeanClass(ReflectUtils.forName(className));
                classDefinition.setLazyInit(false);
                parseProperties(element.getChildNodes(), classDefinition);
                beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
            }
        } else if (ProviderConfig.class.equals(beanClass)) {
            parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
        } else if (ConsumerConfig.class.equals(beanClass)) {
            parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
        }
        Set<String> props = new HashSet<String>();
        ManagedMap parameters = null;
        for (Method setter : beanClass.getMethods()) {
            String name = setter.getName();
            if (name.length() > 3 && name.startsWith("set")
                    && Modifier.isPublic(setter.getModifiers())
                    && setter.getParameterTypes().length == 1) {
                Class<?> type = setter.getParameterTypes()[0];
                String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");
                props.add(property);
                Method getter = null;
                try {
                    getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);
                } catch (NoSuchMethodException e) {
                    try {
                        getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]);
                    } catch (NoSuchMethodException e2) {
                    }
                }
                if (getter == null
                        || !Modifier.isPublic(getter.getModifiers())
                        || !type.equals(getter.getReturnType())) {
                    continue;
                }
                if ("parameters".equals(property)) {
                    parameters = parseParameters(element.getChildNodes(), beanDefinition);
                } else if ("methods".equals(property)) {
                    parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);
                } else if ("arguments".equals(property)) {
                    parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);
                } else {
                    String value = element.getAttribute(property);
         ...
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89

这个类中就引出来dubbo的四大核心:registry,provider,consumer和monitor。
这里写图片描述

这些核心的组件的执行过程,也会在后面分别阐述。

到这里,相信你对dubbo的初始化过程有了一定的理解,希望本文对你有帮助。

                                            <link rel="stylesheet" href="http://csdnimg.cn/release/phoenix/production/markdown_views-68a8aad09e.css">
                                </div>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值