解析spring启动加载dubbo过程
一:简单配置
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
applicationContext.xml
<?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://code.alibabatech.com/schema/dubbo"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 扫描注解 -->
<context:component-scan base-package="per.qiao" />
<!-- 目的:找到注册中心,注册当前服务的基本信息 -->
<!-- 1.配置别名, 目的是在后台可以看到这个服务的别名,好区分到底是谁 -->
<dubbo:application name="serviceImpl"/>
<!-- 2. 配置注册中心 address : 注册中心地址 protocol: 注册中心的协议格式 -->
<dubbo:registry address="192.168.199.247:2181" protocol="zookeeper" />
<!-- 3. 选择需要暴露的方法 interface: 目标类的类型 ref: 目标类的具体实现 timeout: 超时连接时间-->
<dubbo:service interface="per.qiao.service.TestService" ref="serviceImpl" timeout="10000" />
<!-- 配置当前服务暴露的端口 以及暴露协议 -->
<dubbo:protocol name="dubbo" port="9000"/>
</beans>
dubbo的默认文件
spring.handlers文件
http\://dubbo.apache.org/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
spring.schemas文件
http\://dubbo.apache.org/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd
http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/compat/dubbo.xsd
说明: spring.handlers文件用来配置解析dubbo标签并封装成对应的对象
**spring.schemas文件用来配置schame文件的位置 **
当spring容器扫描到配置文件,比如applicationContext时,遇到名称空间xmlns:dubbo="http://code.alibabatech.com/schema/dubbo",就会通过名称空间去查询对应的xsd约束文件,就如schemaLocation中配置的
- xsi:schemaLocation=http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
- 再用这个找到的xsd去spring.schemas文件中找到xsd文件的位置,并校验xsd文件的正确性,返回当前文件(applicationContext.xml)的Document对象
二、启动过程
由ContextLoaderListener进入,调用contextInitialized进入initWebApplicationContext开始spring容器
进入configureAndRefreshWebApplicationContext方法, 然后调用refresh方法
refresh中有个obtainFreshBeanFactory方法,进去
走流水线AbstractApplicationContext->AbstractRefreshableApplicationContext#refreshBeanFactory -> XmlWebApplicationContext#loadBeanDefinitions
进入到XmlBeanDefinitionReader#loadBeanDefinitions(configLocation)
// 遍历你contextConfigLocation配置的多个xml文件
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
来到AbstractBeanDefinitionReader#loadBeanDefinitions-> XmlBeanDefinitionReader#loadBeanDefinitions, doLoadBeanDefinitions, registerBeanDefinitions
看一下doLoadBeanDefinitions 这里会加载你的spring.schame文件
//这个方法会检查你的文件(eg. applicationContext.xml)中的schame的namespace对应的xsd是否正确,返回当前文件的Document对象
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
这个方法是 注册给定DOM文档中包含的bean定义, 也就是解析你的applicationContext.xml中定义的bean
registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws... {
//创建新的doc解析器
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 已有的bean个数
int countBefore = getRegistry().getBeanDefinitionCount();
// 读取document中的bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
- DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
Element root = doc.getDocumentElement();
//读取bean
doRegisterBeanDefinitions(root);
}
- BeanDefinitionParserDelegate#parseCustomElement 这里就从容器中调用到自定义的handler中
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
//获取节点的NamespaceURI, 如果当前节点是dubbo:application 那么就是根据dubbo找到
//xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 后面的uri
String namespaceUri = getNamespaceURI(ele);
//这里的resolve方法,会加载spring.handlers文件,调用namespaceHandler.init();获取当前节点对应的handler解析器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
//parse方法会先根据element的名称获取对应的BeanDefinitionParser
//比如当前元素是dubbo:application, 这里会用application作为key去获取对应的解析器,然后调用其parse方法
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
- DubboBeanDefinitionParser#parse
关于第8步,加载了spring.handlers文件,然后调用DubboNamespaceHandler的init方法,然后是registerBeanDefinitionParser方法,该方法将节点名称和解析封装在NamespaceHandlerSupport的map中
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
然后在handler.parse(...) -> NamespaceHandlerSupport#findParserForElement方法里, 能通过下面的方式获取到
BeanDefinitionParser parser = this.parsers.get(localName);
三、说一个遇到的坑-关于idea的this与toString
在查看解析spring.schames文件的源码时,走到PluggableSchemaResolver类里面,当断点进入resolveEntity方法时,属性private volatile Map<String, String> schemaMappings; 已经有值了,这是不正常的,为此我花了很长时间去找原因
最后发现,是由于idea在debug时,在debugger界面有个this引用着当前对象,它会调用当前类的toString()方法,而且是构造方法执行后每执行一步,它刷新一次(调用toString()),由于toString方法中有调用getSchemaMappings方法,会给你加载数据。 最坑的是,idea调用的用断点拦截不到
你可以尝试着还原这个坑
public class MyTest {
public static void main(String[] args) {
BB b = new BB();
}
static class BB {
private int n = 0;
public BB() {
System.out.println("i am qiao" + n);
}
@Override
public String toString() {
n = 2;
System.out.println("hello");
return "n == " + n;
}
}
}
四、小结:
web.xml中 参数contextConfigLocation的值可以使用,; \t\n(逗号|分号|空格|制表符|换行)分隔开(ContextLoader.INIT_PARAM_DELIMITERS)
classpath:与classpath*: 的区别
classpath: 会从classes目录下获取文件
classpath*: 会从所有路径下加载文件,包括jar包
详见:PathMatchingResourcePatternResolver#getResources
spring.handlers文件用来配置解析dubbo标签的handler
spring.schemas文件用来配置schame文件的位置
PluggableSchemaResolver类解析的spring.shames
DefaultNamespaceHandlerResolver类解析的spring.handler
---恢复内容结束---
解析spring启动加载dubbo过程的更多相关文章
- AngularJS进阶(三十九)基于项目实战解析ng启动加载过程
基于项目实战解析ng启动加载过程 前言 在AngularJS项目开发过程中,自己将遇到的问题进行了整理.回过头来总结一下angular的启动过程. 下面以实际项目为例进行简要讲解. 1.载入ng库 2 ...
- spring启动加载过程源码分析
我们知道启动spring容器两常见的两种方式(其实都是加载spring容器的xml配置文件时启动的): 1.在应用程序下加载 ApplicationContext ctx = new ClassPat ...
- springboot集成mybatis源码分析-启动加载mybatis过程(二)
1.springboot项目最核心的就是自动加载配置,该功能则依赖的是一个注解@SpringBootApplication中的@EnableAutoConfiguration 2.EnableAuto ...
- Spring 启动加载资源到内存
前言 在一些业务场景中,当容器初始化完成之后,需要处理一些操作,比如一些数据的加载.初始化缓存.特定任务的注册等等.我找到了三种方式解决下面的问题. 1.使用PostConstruct注解 这种解决方 ...
- spring启动加载类,手动加载bean
方法一: public final class Assembler implements BeanFactoryPostProcessor { private static ConfigurableL ...
- Tomcat源码分析三:Tomcat启动加载过程(一)的源码解析
Tomcat启动加载过程(一)的源码解析 今天,我将分享用源码的方式讲解Tomcat启动的加载过程,关于Tomcat的架构请参阅<Tomcat源码分析二:先看看Tomcat的整体架构>一文 ...
- Spring源码解析-配置文件的加载
spring是一个很有名的java开源框架,作为一名javaer还是有必要了解spring的设计原理和机制,beans.core.context作为spring的三个核心组件.而三个组件中最重要的就是 ...
- redis启动加载过程、数据持久化
背景 公司一年的部分业务数据放在redis服务器上,但数据量比较大,单纯的string类型数据一年就将近32G,而且是经过压缩后的. 所以我在想能否通过获取string数据的时间改为保存list数据类 ...
- spring boot启动加载项CommandLineRunner
spring boot启动加载项CommandLineRunner 在使用SpringBoot构建项目时,我们通常有一些预先数据的加载.那么SpringBoot提供了一个简单的方式来实现–Comman ...
随机推荐
- java maven scope compile,provide,system,test,runtime
在一个maven项目中,如果存在编译需要而发布不需要的jar包,可以用scope标签,值设为provided.如下: <dependency> <groupId ...
- 2018-2019-2 20165234 《网络对抗技术》 Exp9 Web安全基础
Exp9 Web安全基础 一. 实践内容 1. 安装JDK.Webgoat 2. SQL注入攻击 数字型注入(Numeric SQL Injection) 日志欺骗(Log Spoofing) 字符串 ...
- [Ubuntu] 14.04版本安装JDK8失败
系统:Ubuntu 14.04 编译 Android 7.1 系统需要 JDK 8,但是安装时失败, $ -jdk Reading package lists... Done Building dep ...
- Mininet系列实验(六):Mininet动态改变转发规则实验
一. 实验目的 熟悉Mininet自定义拓扑脚本的编写:熟悉编写POX脚本动态改变转发规则 二.实验原理 在SDN环境中,控制器可以通过对交换机下发流表操作来控制交换机的转发行为.在本实验中,基于Mi ...
- Matlab 图像平移、旋转、缩放、镜像
今天学习了用Matlab实现对图像的基本操作.在Matlab中,图像是按照二维矩阵的形式表示的.所以对图像的操作就是对矩阵的操作. 对图像进行缩放.平移.旋转,都可以转化为矩阵的运算. 关于变换矩阵的 ...
- CORTEX-M3中断的现场保护问题
在<Cortex-M3 Devices Generic User Guide.pdf>中介绍了异常入栈和出栈的情况,详见2.3 Exception model.Cortex-M3内核的寄存 ...
- OpenJudge计算概论-细菌实验分组
/*====================================================细菌实验分组总时间限制: 1000ms 内存限制: 65536kB描述有一种细菌分为A.B两 ...
- keras启用tensorboard
在callback函数中添加tensorboard,启用tensorboard. # TensorBoard callback tensorboard_cb = K.callbacks.TensorB ...
- fileBeat的简单使用
Beat的简单使用 Filebeat配置 Output 常见日志格式封装 简单使用filebeat格式化nginx日志 Filebeat的配置: # 修改filebeat.yml # vim file ...
- 目标检测中的选择性搜索-selective search-没弄
https://blog.csdn.net/small_munich/article/details/79595257 https://www.cnblogs.com/zyly/p/9259392.h ...