dubbo配置解析
文章系列
【一、dubbo源码解析之框架粗谈】
【二、dubbo源码解析之dubbo配置解析】
【三、dubbo源码解析之服务发布与注册】
【四、dubbo源码解析之服务发现】
【五、dubbo源码解析之服务调用(通信)流程】
【六、dubbo获取服务提供者IP列表】
一、Spring 解析自定义配置
我们在 Spring 的 xml 配置文件里经常定义各种各样的配置(tx、bean、mvc、bean等等),以及集成第三方框架时,也会看到一些 Spring 之外的配置,例如 mybatis 的配置、dubbo 的配置、redis 的配置等等。
看到这里,是否会引发我们思考,Spring 是如何解析这些 xml 配置文件呢???
在 Spring 我们通过创建一个 ClassPathXmlApplicationContext
来解析、加载以及初始化 Spring 上下文,其构造方法代码如下:
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
// 重启、刷新、重置
refresh();
}
}
}
在其构造方法中,会调用一个 refresh()
方法,进行 Spring 重启、刷新、重置动作,这也是 Spring 的入口方法。
在 refresh()
方法中,会加载扫描所有 META-INF/spring.handlers
文件,调用链路如下:
ClassPathXmlApplicationContext
↓
AbstractApplicationContext#refresh() // 刷新
↓
AbstractApplicationContext#obtainFreshBeanFactory()
↓
AbstractRefreshableApplicationContext#refreshBeanFactory() // 刷新
↓
AbstractXmlApplicationContext#loadBeanDefinitions(DefaultListableBeanFactory beanFactory) // 加载 BeanDefinition
↓
AbstractXmlApplicationContext#loadBeanDefinitions(XmlBeanDefinitionReader reader) // 加载xml 中的 BeanDefinition
↓
AbstractBeanDefinitionReader#loadBeanDefinitions(Resource... resources)
↓
XmlBeanDefinitionReader#loadBeanDefinitions(Resource resource)
↓
XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource encodedResource)
↓
XmlBeanDefinitionReader#doLoadBeanDefinitions(InputSource inputSource, Resource resource) // 加载xml 中的 BeanDefinition
↓
XmlBeanDefinitionReader#registerBeanDefinitions(Document doc, Resource resource) // 解析doc,注册对应的 BeanDefinition
↓
DefaultBeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
↓
DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions(Element root) // 解析doc,注册对应的 BeanDefinition
DefaultBeanDefinitionDocumentReader#parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) // 解析
↓
BeanDefinitionParserDelegate#parseCustomElement(Element ele) // 没有使用Spring默认的XML命名空间,则使用用户自定义的解析规则解析元素节点
// 如果使用Spring的Bean规则解析元素节点,则调用DefaultBeanDefinitionDocumentReader#parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) 方法
↓
BeanDefinitionParserDelegate#parseCustomElement(Element ele, @Nullable BeanDefinition containingBd)
↓
DefaultNamespaceHandlerResolver#resolve(String namespaceUri) // 根据namespaceUri 获取对应的NamespaceHandler
↓
DefaultNamespaceHandlerResolver#getHandlerMappings() // 获取所有 handlerMappings 映射
DefaultNamespaceHandlerResolver#getHandlerMappings()
方法,获取所有 handlerMappings 映射。
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
// 默认Handler映射文件
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
public DefaultNamespaceHandlerResolver() {
this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
private Map<String, Object> getHandlerMappings() {
Map<String, Object> handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
synchronized (this) {
handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
try {
// 将classpath下的 META-INF/spring.handlers 中的配置加载到Map中
// this.handlerMappingsLocation = META-INF/spring.handlers
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map<String, Object> mappingsToUse = new ConcurrentHashMap<>(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, mappingsToUse);
// key -> NamespaceUrl
// value -> NamespaceHandler
handlerMappings = mappingsToUse;
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return handlerMappings;
}
Spring 在启动过程中,将所有 classpath 下的 META-INF/spring.handlers
文件中的配置加载到一个 Map<String, Object> handlerMappings
中,该 Map 存储了从命名空间 URI 到 NamespaceHandler 类名实例的映射,其中 NamespaceUrl 为key,对应的 NamespaceHandler 为value,例如 dubbo 中的 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
最后,会在 DefaultNamespaceHandlerResolver#resolve(String namespaceUri)
方法中,调用 handlerMappings
中的 NamespaceHandler
的 init()
方法,逻辑如下:
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
public NamespaceHandler resolve(String namespaceUri) {
// 获取handlerMappings
Map<String, Object> handlerMappings = getHandlerMappings();
// 通过 namespaceUri,获取对应的 NamespaceHandler
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);
// 调用其 init() 方法,完成初始化
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);
}
}
}
}
二、编写一个 Spring 自定义配置
完成一个Spring的自定义配置一般需要下面几步:
- 定义Java Bean
- 编写XSD(XML Schema)文件,用于校验XML
- 编写 NameSpaceHandler 和 BeanDefinitionParser 完成解析工作
- 编写
spring.handlers
和spring.schemas
串联起所有部件 - 编写自定义配置文件xml
- 应用
2.1 定义Java Bean
@Data
public class People {
private String id;
private String name;
private Integer age;
}
2.2 编写XSD(XML Schema)文件
用于校验XML,定义了一些列的语法来规范XML,存放在 resources/META-INF/
目录下,如:
people.xsd
内容如下:
<xsd:schema xmlns="http://code.test.com/schema/people"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://code.test.com/schema/people"
elementFormDefault="qualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:element name="people">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="age" type="xsd:int"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>
2.3 编写 NameSpaceHandler 和 BeanDefinitionParser 完成解析工作
NameSpaceHandler
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class MyNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("people", new PeopleBeanDefinitionParser());
}
}
PeopleBeanDefinitionParser
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
public class PeopleBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class getBeanClass(Element element){
return People.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder bean){
String name = element.getAttribute("name");
String age = element.getAttribute("age");
String id = element.getAttribute("id");
if (StringUtils.hasText(name)) {
bean.addPropertyValue("name",name);
} if (StringUtils.hasText(age)) {
bean.addPropertyValue("age",Integer.valueOf(age));
} if (StringUtils.hasText(id)) {
bean.addPropertyValue("id",id);
}
}
}
2.4 编写 spring.handlers
和 spring.schemas
编写 spring.handlers
和 spring.schemas
,用于串联所有部件,存放在 resources/META-INF/
目录下,如:
spring.handlers 内容如下:
http\://code.test.com/schema/people=com.example.schema.MyNamespaceHandler
spring.schemas 内容如下:
http\://code.test.com/schema/people.xsd=META-INF/people.xsd
2.5 编写自定义配置文件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:test="http://code.test.com/schema/people"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://code.test.com/schema/people http://code.test.com/schema/people.xsd
">
<test:people id="people" name="testName" age="100"/>
</beans>
2.6 应用
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SchemaMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/MyName.xml");
People people =(People) context.getBean("people");
System.out.println(people.getName());
}
}
三、dubbo 中的 Spring 配置
dubbo 通过 Spring 提供的自定义配置解析,完成了与 Spring 的整合,内容如下:
其中 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
3.1 DubboNamespaceHandler
Spring 在装载 DubboNamespaceHandler
时,会自动调用其 init()
方法,通过注册一个BeanDefinitionParser
解析器,完成Bean对象的注册,如下:。
public class DubboNamespaceHandler extends NamespaceHandlerSupport implements ConfigurableSourceBeanMetadataElement {
@Override
public void init() {
// 注册 Bean 定义解析器
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));
registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.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, true));
registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
}
}
3.2 DubboBeanDefinitionParser
Spring 会自动调用 BeanDefinitionParser
中的 BeanDefinition parse(Element element, ParserContext parserContext)
方法,解析指定的 Element ,并注册到其 IOC 容器中。
public class DubboBeanDefinitionParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 解析
return parse(element, parserContext, beanClass, required);
}
}