这篇文章是 Dubbo 源码分析的开端, 与其说这篇文章是 Dubbo 源码分析, 不如是 spring 源码分析, 因为大部分都是在分析 spring 如何解析 xml 配置文件的, 为了与后面的 Dubbo 源码分析保持一致, 姑且这样命名了.
使用 Dubbo 框架开发分布式服务时, 一般使用 spring 进行管理, 在 spring 的配置文件中进行配置, 例如服务提供者 Provider 端配置如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
- <!-- 提供方应用信息, 用于计算依赖关系 -->
- <dubbo:application name="hello-world-app" />
- <!-- 使用 multicast 广播注册中心暴露服务地址 -->
- <dubbo:registry address="multicast://224.5.6.7:1234" />
- <!-- 用 dubbo 协议在 20880 端口暴露服务 -->
- <dubbo:protocol name="dubbo" port="20880" />
- <!-- 声明需要暴露的服务接口 -->
- <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />
- <!-- 和本地 bean 一样实现服务 -->
- <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />
- </beans>
对于
- (省略描述....)
- public interface NamespaceHandler {
- void init();
- BeanDefinition parse(Element element, ParserContext parserContext);
- BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
- }
可以看出其中有一个 parse 方法, 它的作用就是用于解析 Xml 文档中的节点. 这个接口有一个抽象的实现类 NamespaceHandlerSupport, 具体的标签解析器都继承这个抽象类, 我们来看下有哪些:
可以看见我们熟悉的 Aop 还有 Dubbo NamespaceHandler. 那 Spring 是如何知道要使用 Dubbo 定义的 handler 来解析自定义的标签呢? 他们的结合点就在一个配置文件, spring 留了一个配置文件, 只要我们配置了它, spring 就可以找到. 这个配置文件名字叫做 spring.handlers,spring 在解析 xml 文件时, 会去加载 spring.handlers 配置文件, 然后寻找能够解析自定义标签的 handler.
spring.handlers 在 Dubbo 中是怎样的内容呢, 我们一起来看下:
路径在: dubbo-config/dubbon-config-spring/META-INF/spring.handlers
内容如下:
- http://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
- http://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
我们来看一下 spring 是如何找到这个文件并生成对应标签 namespaceHandler 的:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"application.xml"});
我们使用 spring 框架, 启动的时候都会写这么一句话, 加载 xml 配置文件. 当执行到 AbstractXmlApplicationContext 类的 loadBeanDefinitions 方法时, 会创建一个 XmlBeanDefinitionReader 对象, 读取读取并解析 xml.
- /**
- * Loads the bean definitions via an XmlBeanDefinitionReader.
- * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
- * @see #initBeanDefinitionReader
- * @see #loadBeanDefinitions
- */
- @Override
- protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
- // Create a new XmlBeanDefinitionReader for the given BeanFactory.
- XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
- // Configure the bean definition reader with this context's
- // resource loading environment.
- beanDefinitionReader.setEnvironment(this.getEnvironment());
- beanDefinitionReader.setResourceLoader(this);
- beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
- // Allow a subclass to provide custom initialization of the reader,
- // then proceed with actually loading the bean definitions.
- initBeanDefinitionReader(beanDefinitionReader);
- loadBeanDefinitions(beanDefinitionReader);
- }
最后一句话 loadBeanDefinitions(beanDefinitionReader), 是使用 XmlBeanDefinitionReader 将 xml 解析成 BeanDefinition.
接下来调用 XmlBeanDefinitionReader 的 registerBeanDefinitions 方法创建 BeanDefinitionDocumentReader 对象, 这个对象才是真正解析 XML 的对象.
- public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
- BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
- int countBefore = getRegistry().getBeanDefinitionCount();
- // 创建 ReaderContext, 并调用 documentReader 的 registerBeanDefinitions 方法
- documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
- return getRegistry().getBeanDefinitionCount() - countBefore;
- }
大家再来看下 createReaderContext 方法
- /**
- * Create the {@link XmlReaderContext} to pass over to the document reader.
- */
- public XmlReaderContext createReaderContext(Resource resource) {
- return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
- this.sourceExtractor, this, getNamespaceHandlerResolver());
- }
终于出现了 NamespaceHandler, 那就是 getNamespaceHandlerResolver() 方法, 再来看下这个方法
- public NamespaceHandlerResolver getNamespaceHandlerResolver() {
- if (this.namespaceHandlerResolver == null) {
- this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
- }
- return this.namespaceHandlerResolver;
- }
- protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
- return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
- }
- public DefaultNamespaceHandlerResolver(ClassLoader classLoader) {
- this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
- }
- // 最终会调用这个构造器实例化 DefaultNamespaceHandlerResolver 类, 此时 handlerMappingsLocation 成员变量的值为 META-INF/spring.handlers 了
- public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
- Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
- this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
- this.handlerMappingsLocation = handlerMappingsLocation;
- }
- /**
- * The location to look for the mapping files. Can be present in multiple JAR files.
- */
- public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
- // 读取 META-INF/spring.handlers 配置文件的内容
- private Map<String, Object> getHandlerMappings() {
- if (this.handlerMappings == null) {
- synchronized (this) {
- if (this.handlerMappings == null) {
- try {
- Properties mappings =
- PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
- if (logger.isDebugEnabled()) {
- logger.debug("Loaded NamespaceHandler mappings:" + mappings);
- }
- Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
- CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
- this.handlerMappings = handlerMappings;
- }
- catch (IOException ex) {
- throw new IllegalStateException(
- "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
- }
- }
- }
- }
- return this.handlerMappings;
- }
会创建一个默认的 DefaultNamespaceHandlerResolver 对象, 其中有一个变量 DEFAULT_HANDLER_MAPPINGS_LOCATION, 其值是 META-INF/spring.handlers, 此时可以看到 spring 会去加载 spring.hadlers 中的内容.
那加载完后, 会在什么地方使用呢?
我们来看一下 DefaultBeanDefinitionDocumentReader 类中的 parseBeanDefinitions 方法
- protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
- if (delegate.isDefaultNamespace(root)) {
- NodeList nl = root.getChildNodes();
- for (int i = 0; i <nl.getLength(); i++) {
- Node node = nl.item(i);
- if (node instanceof Element) {
- Element ele = (Element) node;
- // 判断标签是标准的标准还是自定义的, 判断的依据就是标准的 namespace 是否是 http://www.springframework.org/schema/beans
- // 如果是, 则是标准的标签, 如果不是则不是标准的标签
- if (delegate.isDefaultNamespace(ele)) {
- parseDefaultElement(ele, delegate);
- }
- else {
- delegate.parseCustomElement(ele);
- }
- }
- }
- }
- else {
- delegate.parseCustomElement(root);
- }
- }
接下来看一下 BeanDefinitionParserDelegate 类的 parseCustomElement 方法, BeanDefinitionParserDelegate 对象是在 DefaultBeanDefinitionDocumentReader 的 doRegisterBeanDefinitions 方法中生成的
, 它的作用是用来解析标签的.
- public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
- // 根据标签得到标签的 namespace
- //dubbo 自定义的标签得到的 namespace 是 http://dubbo.apache.org/schema/dubbo
- String namespaceUri = getNamespaceURI(ele);
- NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
- if (handler == null) {
- error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
- return null;
- }
- return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
- }
接下来看下 DefaultBeanDefinitionDocumentReader 的 resolve 方法
- @Override
- public NamespaceHandler resolve(String namespaceUri) {
- // 得到加载的内容
- Map<String, Object> handlerMappings = getHandlerMappings();
- // 根据标签的 namspace 得到 handler 类名
- //dubbo 的 namespace 为 http://dubbo.apache.org/schema/dubbo
- Object handlerOrClassName = handlerMappings.get(namespaceUri);
- if (handlerOrClassName == null) {
- return null;
- }
- else if (handlerOrClassName instanceof NamespaceHandler) {
- return (NamespaceHandler) handlerOrClassName;
- }
- else {
- String className = (String) handlerOrClassName;
- try {
- Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
- if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
- throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
- "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
- }
- // 实例化 namespaceHandler
- NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
- namespaceHandler.init();
- handlerMappings.put(namespaceUri, namespaceHandler);
- return namespaceHandler;
- }
- catch (ClassNotFoundException ex) {
- throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
- namespaceUri + "] not found", ex);
- }
- catch (LinkageError err) {
- throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
- namespaceUri + "]: problem with handler class file or dependent class", err);
- }
- }
- }
得到 DubboNamespaceHandler 实例后, 调用其 init() 方法
- @Override
- public void init() {
- // 将 application,module 等名称做为 key 注册到解析器 Map 中
- // 这些 key 名称正是 dubbo 自定义标签的 localName, 例如 < dubbo:application /> 标签的 LocalName 是 application
- 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 AnnotationBeanDefinitionParser());
- }
可以看到所有的 key(除了 annotation) 对应的标签解析器都是 DubboBeanDefinitionParser 实例对象, 至于 DubboBeanDefinitionParser 是如何解析标签的, 这里就不做分析了, 如果想要了解, 可以看看他的源码.
来源: https://www.cnblogs.com/mycodingworld/p/dubbo_xmlreader.html