dubbo源码分析4(spring配置文件解析机制)

我们知道dubbo一般也不会单独使用的吧,都会和spring一起使用,知道为什么吗?

因为dubbo是基于spring的扩展机制进行扩展的,所以首先我们要知道spring提供了一种什么扩展机制?

先看下图,基于spring的配置文件都会有如下所示这段东西,这是干啥的呢?

dubbo源码分析4(spring配置文件解析机制)

1.spring配置文件的文件头

首先我为了偷懒,要去找一个spring配置文件的网图,下图所示,这是一个很常见的spring配置文件,但是前面那一堆xmlns是什么东西啊,我擦(╯—﹏—)╯(┷━━━┷

而且我们看看上述的标签,可以分为三块,第一部分是文件头(也就是xmlns那一堆), 第二部分是这种bean的标签,第三部分是这类标签名字还带有冒号,冒号还跟着一个奇怪的东西的๑乛◡乛๑

1.1 xmlns部分

xmlns全程是xml namespace,翻译一下就是xml命名空间,你非要问这个有什么用?其实啥用没有,你把他看成一个键值对,key–>uuid(例如下图tx—>http://www.springframework.org/schema/tx), 键表示标签的前缀,值就是一个唯一标识,下图步骤1所示

但是这个唯一标识的话,还需要在xsi:schemaLocation中对应起来,如步骤2所示;

而且每一个唯一标识还会对应一个xsd文件,步骤3所示

每一个xsd文件中就是描述了当前命名空间中指定的标签中,规范了各个属性,步骤4所示

dubbo源码分析4(spring配置文件解析机制)

说出来你可能不信,现在根据上图步骤4的url找到xsd文件(默认先从jar包中找,没有的话,才会去网络上下载)

注意,这个文件其实在spring-tx.jar中META-INF/spring.schemas中可以找到本地该文件的地址,如果没有,才会联网去spring官方地址那里去下载

dubbo源码分析4(spring配置文件解析机制)

我们轻轻一点开这个xsd文件康康,随意看看就能看到

注意:spring配置文件中命名空间xmlns:tx=”http://www.springframework.org/schema/tx” 要和xsd文件头中的xmlns 、targetNamespace保持一致的呀

dubbo源码分析4(spring配置文件解析机制)

1.2 bean标签

上面说了一大堆没啥用的东西,我们可以在spring配置文件中看到

dubbo源码分析4(spring配置文件解析机制)

1.3 带有标签前缀的标签

我擦,好像已经在1.1中说过了,那么我们就过\(@ ̄∇ ̄@)/,其实我很想写的更多,但是这里空白太小了,写不下,嘿嘿~

2.自定义spring标签栗子

说了这么多,我们自己来捣鼓一个自定义标签出来,目录结构如下:

dubbo源码分析4(spring配置文件解析机制)

2.1.接口和实现类:

java;gutter:true; public interface MenuService { void sayHello(); }</p> <p>public class MenuServiceImpl implements MenuService{ @Override public void sayHello() { System.out.println("hello world"); } }</p> <pre><code> **2.2. 编写app.xml文件** ;gutter:true;
xmlns:myrpc="http://com.protagonist.com/schema"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://com.protagonist.com/schema http://com.protagonist.com/schema/rpc.xsd">

<myrpc:reference id="menuService" interface="com.protagonist.springload.MenuService" />

2.3 编写spring.handlers文件

描述上面那个命名空间xmlns:myrpc对应的命名空间处理器,以及标签解析器

java;gutter:true; http\://com.protagonist.com/schema=com.protagonist.config.RPCNamespaceHandler</p> <pre><code> 下面的代码可能略多,其实核心的就是在解析到reference标签的时候,获取到interface属性的接口全路径,然后使用jdk动态代理对这个接口生成一个动态代理类 ;gutter:true;
package com.protagonist.config;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class RPCNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
/**
* 在app.xml文件中解析当前命名空间所在的标签的时候,有reference属性,就会RPCBeanDefinitionParser解析起去解析该标签
*
*/
registerBeanDefinitionParser("reference", new RPCBeanDefinitionParser());
}
}

这里的getBeanClass方法中,注意返回的对象是ReferenceBean.class

java;gutter:true; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.util.StringUtils; import org.w3c.dom.Element;</p> <p>public class RPCBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { /*<em> * app.xml配置文件中, 解析标签对应的javaBean对象 * @param element * @return </em>/ protected Class getBeanClass(Element element) { return ReferenceBean.class; }</p> <pre><code>/** * 解析标签,取出interface属性的值 * @param element * @param bean */ protected void doParse(Element element, BeanDefinitionBuilder bean) { String interfaceClass = element.getAttribute("interface"); if (StringUtils.hasText(interfaceClass)) { bean.addPropertyValue("interfaceClass", interfaceClass); } } </code></pre> <p>}</p> <pre><code> ReferenceBean类实现了FactoryBean接口,只要spring容器初始化创建bean实例的话就会调用getObject方法 ;gutter:true;
import org.springframework.beans.factory.FactoryBean;

public class ReferenceBean extends ReferenceConfig implements FactoryBean {
@Override
public Object getObject() throws Exception {
return get();
}
@Override
public Class getObjectType() {
return getInterfaceClass();
}
@Override
public boolean isSingleton() {
return true;
}
}

ReferenceConfig类其实就是根据接口的全名称,生成一个动态代理类

java;gutter:true; import com.protagonist.springload.ProxyFactory;</p> <p>public class ReferenceConfig { private Class interfaceClass; /*<em> * 接口代理类引用 </em>/ private transient volatile T ref; public synchronized T get() { if (ref == null) { init(); } return ref; }</p> <pre><code>/** * 将xml文件中的接口的全路径,使用jdk动态代理生成一个代理对象 */ private void init() { ref = new ProxyFactory(interfaceClass).getProxyObject(); } public Class getInterfaceClass() { return interfaceClass; } public void setInterfaceClass(Class interfaceClass) { this.interfaceClass = interfaceClass; } </code></pre> <p>}</p> <pre><code> **ProxyFactory类,其实就是封装了一下jdk动态代理,在下面的invoke方法中,我们可以使用Socket去连接远程服务器,进行交互,获取响应数据** ;gutter:true;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory implements InvocationHandler {
private Class interfaceClass;
public ProxyFactory(Class interfaceClass) {
this.interfaceClass = interfaceClass;
}
/**
* 返回代理对象,此处用泛型为了调用时不用强转,用Object需要强转
*/
public T getProxyObject(){
return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(),//类加载器
new Class[]{interfaceClass},//为哪些接口做代理
this);//(把这些方法拦截到哪处理)
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
System.out.println("将要发送的数据进行编码");
System.out.println("开始发送网络请求");
System.out.println("获取响应数据");
System.out.println("开始解码,获取明文数据");
return null;
}
}

2.4.编写spring.schemas文件

用于描述上面app.xml文件中绿色部分对应的xsd文件的实际位置

java;gutter:true; http\://com.protagonist.com/schema/rpc.xsd=META-INF/rpc.xsd</p> <pre><code> **2.5 编写xsd文件** 用于描述当前命名空间的标签内都有啥属性,注意下面xmls和targetNamespace要和app.xml文件中保持一致 **2.6 单元测试类以及结果** ;gutter:true;
import com.protagonist.springload.MenuService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:app.xml")
public class DemoTest {

@Resource
private MenuService menuService;

@Test
public void testSpring(){
menuService.sayHello();
}
}

dubbo源码分析4(spring配置文件解析机制)

3.解析spring配置文件的原理

就使用上面举了一个很简单的栗子,我们调试一下,看看spring的xml配置文件中我们自定义的那个标签,是怎么解析的呢?

这个时候对spring源码了解过的小伙伴肯定就会跳出来说,不就是会把配置文件中的一个个bean解析成BeanDefinition对象么?道友我五百年前就知道了( ̄o ̄) . z Z

这就很不给面子呀,我擦,对于这种小伙伴,我都会把你毒打一顿,让你体会社会的险恶๑乛◡乛๑

言归正传,我们就大概看看是怎么从一个xml文件编程BeanDefinition的吧,我们只看大概流程,毕竟这不是讲spring源码的….

dubbo源码分析4(spring配置文件解析机制)

3.1 spring容器初始化的过程,会将app.xml文件封装为Resource对象

dubbo源码分析4(spring配置文件解析机制)

dubbo源码分析4(spring配置文件解析机制)

dubbo源码分析4(spring配置文件解析机制)

dubbo源码分析4(spring配置文件解析机制)

3.4 将配置文件META-INF/spring.handler中内容转为Properties对象,这个类就是继承了HashTable,就是一个Map

dubbo源码分析4(spring配置文件解析机制)

dubbo源码分析4(spring配置文件解析机制)

3.5. 前面几步就获取到了命名空间处理器的全类名,这里就是使用反射进行实例化,执行初始化方法

dubbo源码分析4(spring配置文件解析机制)

3.6 到了我们自定义命名空间处理器重写的方法中,然后就是实例化RPCBeanDefinitionParser,后续初始化的流程会执行RPCBeanDefinitionParser的doParse方法,由于篇幅有限,有兴趣的可以自己调试

dubbo源码分析4(spring配置文件解析机制)

4.总结

这篇写的还是蛮多的,其实就是简单的使用了一下spring的自定义标签的功能,我们自己也简单的实现了一个没什么用的超级简易版的dubbo远程调用的mock(虽然说还没有真正的去调用,哈哈哈),dubbo实现的大概思路就是这个样子;

就是根据dubbo的配置文件,找到我们要引用的接口的全路径,然后使用动态代理生成对象,去注册中心中找到该接口和方法的所在的服务器的ip和端口,然后通过建立tcp连接的方式去向那个服务器发送数据并得到响应,然后解析数据;

说起来是不是很简单,但是其中我们要考虑的东西特别多,比如注册中心用啥?注册中心挂了怎么办?序列化方式?远程调用服务时候负载均衡?超时时间?容错方案?通讯协议?等等问题都需要考虑到,后续我们慢慢说

Original: https://www.cnblogs.com/wyq1995/p/15631155.html
Author: java小新人
Title: dubbo源码分析4(spring配置文件解析机制)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/574196/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Docker 核心知识回顾

    Docker 核心知识回顾 最近公司为了提高项目治理能力、提升开发效率,将之前的CICD项目扩展成 devops进行项目管理。开发人员需要对自己的负责的项目进行流水线的部署,包括写…

    Java 2023年6月7日
    098
  • 自己设计大学排名-数据库实践

    一、操作数据库(以SQLite3为例): SQLite3 可使用 sqlite3 模块与 Python 进行集成。它提供了一个与 PEP 249 描述的 DB-API 2.0 规范…

    Java 2023年6月6日
    070
  • OSGI企业应用开发(十五)基于Spring、Mybatis、Spring MVC实现一个登录应用

    前面文章中,我们已经完成了OSGI应用中Spring、Mybatis、Spring MVC的整合,本篇文章我们就在这个基础上来完成一个简单的登录应用,其中用户名和密码需要从数据库中…

    Java 2023年5月30日
    098
  • Apache DolphinScheduler新一代分布式工作流任务调度平台实战-中

    @ 架构设计 总体架构 启动流程图 架构设计思想简述 负载均衡 缓存 实战使用 参数 参数优先级 内置参数 基础内置参数 衍生内置参数 本地参数和全局参数 工作流传参 数据源管理 …

    Java 2023年6月5日
    0104
  • mysql面试题整理

    1 myisam 和 innodb 引擎的区别 innodb 支持事务,外键,myisam 不支持 innodb 支持 mvcc ,myisam 不支持 innodb 支持表锁、行…

    Java 2023年6月5日
    0123
  • Prometheus 安装

    官方文档 https://prometheus.io/docs/introduction/first_steps/ 中文文档 https://prometheus.fuckclou…

    Java 2023年6月9日
    0100
  • ToneGenerator Init failed Crash 崩溃

    需求需要在扫码时产生一个短促的提示音, 搜了下像这样实现。测试时发现多次扫码后,会触发程序崩溃问题。 异常如下 代码如下: 一番搜索, 以下为最佳答案, 加上以后,循环测试, 不再…

    Java 2023年6月15日
    062
  • java使用freemarker作为模板导出Excel表格

    1:首先新建一个excel表格自己弄好格式如下图 2:把excel 表格另存为xml格式文件如下图 3:这个时候的文件就是xml 格式的文件了,在myeclipse里面项目工程里面…

    Java 2023年5月29日
    099
  • 公众号文章汇总

    JDK源码分析实战系列-ThreadLocal自旋锁-JUC系列Doug Lea文章阅读记录-JUC系列AQS源码一窥-JUC系列AQS源码二探-JUC系列AQS源码三视-JUC系…

    Java 2023年6月14日
    083
  • 图解HTTP阅读笔记

    chapter1.了解Web及网络基础 URI(Uniform Resource Identifier):统一资源定位符,由某个协议名称表示的资源的定位标识符。协议名称比如说HTT…

    Java 2023年6月15日
    086
  • Caffeine缓存框架入门学习

    Caffeine缓存框架入门学习和常用API 引入依赖 com.github.ben-manes.caffeine caffeine 2.5.5 基础创建方式 Cache cach…

    Java 2023年6月13日
    085
  • 朱晔和你聊Spring系列S1E5:Spring WebFlux小探

    阅读PDF版本 本文会来做一些应用对比Spring MVC和Spring WebFlux,观察线程模型的区别,然后做一下简单的压力测试。 创建一个传统的Spring MVC应用 先…

    Java 2023年5月30日
    093
  • 这个队列的思路是真的好,现在它是我简历上的亮点了。

    你好呀,我是歪歪。 前几天在一个开源项目的 github 里面看到这样的一个 pr: 光是看这个名字,里面有个 MemorySafe,我就有点陷进去了。 我先给你看看这个东西: 这…

    Java 2023年6月5日
    061
  • Java中的4种 I/O 模型

    博客园 :当前访问的博文已被密码保护 请输入阅读密码: Original: https://www.cnblogs.com/franson-2016/p/13262212.html…

    Java 2023年5月29日
    090
  • 多线程编程(2) – 一切从 CreateThread 开始

    产生一个线程 产生一个线程,是以 CreateThread() 作为一切行动的开始。此函数的原型如下: function CreateThread( lpThreadAttribu…

    Java 2023年5月30日
    066
  • AWS Lambda 借助 Serverless Framework,迅速起飞

    前言 微服务架构有别于传统的单体式应用方案,我们可将单体应用拆分成多个核心功能。每个功能都被称为一项服务,可以单独构建和部署,这意味着各项服务在工作时不会互相影响 这种设计理念被进…

    Java 2023年6月5日
    077
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球