xml配置
我们先来看看服务提供者和服务消费者是如何配置xml的
(1)服务提供者配置文件
<?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-provider" />
<!-- 使用multicast广播注册中心暴露服务地址 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService" />
<!-- 和本地bean一样实现服务 -->
<bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl" />
</beans>
(2)服务消费者配置文件
<?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-consumer" />
<!-- 使用multicast广播注册中心暴露发现服务地址 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
<dubbo:reference id="demoService" interface="org.apache.dubbo.demo.DemoService" />
</beans>
(3)服务提供者
public class XmlProvider {
public static void main(String[] args) {
System.setProperty("java.net.preferIPv4Stack", "true");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
context.start();
System.out.println("Provider started.");
}
}
那dubbo是如何解析这些标签的呢?
源码分析
(1)进入BeanDefinitionParserDelegate,执行parseCustomElement方法,通过this.readerContext.getNamespaceHandlerResolver()进入DefaultNamespaceHandlerResolver。
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = this.getNamespaceURI(ele);
//关键
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null; } else {
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
}
(2)进入DefaultNamespaceHandlerResolver,执行resolve方法,解析进入DubboNamespaceHandler。
public NamespaceHandler resolve(String namespaceUri) {
Map<String, Object> handlerMappings = this.getHandlerMappings();
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");
} else {
NamespaceHandler namespaceHandler = (NamespaceHandler)BeanUtils.instantiateClass(handlerClass);
//关键
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
} catch (ClassNotFoundException var7) {
throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", var7);
} catch (LinkageError var8) {
throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", var8);
}
}
}
this.handlerMappings是一个Map集合,为空的时候,就会去各个包下面的META-INF/spring.handlers中加载。
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
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 (this.logger.isDebugEnabled()) {
this.logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map<String, Object> handlerMappings = new ConcurrentHashMap(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
} catch (IOException var5) {
throw new IllegalStateException("Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", var5);
}
}
}
}
return this.handlerMappings;
}
(3)进入解析得到DubboNamespaceHandler,执行init方法,将标签关联到解析实现的类。
public void init() {
this.registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
this.registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
this.registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
this.registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
this.registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
this.registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
this.registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
this.registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
this.registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
this.registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
}
(4)DubboNamespaceHandler继承了NamespaceHandlerSupport,再来看看registerBeanDefinitionParser在NamespaceHandlerSupport中是如何定义的:
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
(5)在BeanDefinitionParserDelegate.parseCustomElement()中通过handler.parse()方法进入NamespaceHandlerSupport。调用NamespaceHandlerSupport的findParserForElement返回DubboBeanDefinitionParser,然后调用DubboBeanDefinitionParser的parse方法。
public BeanDefinition parse(Element element, ParserContext parserContext) {
return this.findParserForElement(element, parserContext).parse(element, parserContext);
}
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = (BeanDefinitionParser)this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
(6)进入DubboBeanDefinitionParser,执行parse方法
public BeanDefinition parse(Element element, ParserContext parserContext) {
return parse(element, parserContext, beanClass, required);
}
执行过程如下:
- 实例化一个RootBeanDefinition,获取当前标签节点的属性id的值;如果id没有设置值,并且在(3)中设置的required的值为true,就去获取标签节点的属性name的值;如果name的值为空,判断实例Class是否是ProtocolConfig,如果是,就把generatedBeanName设置为dubbo,如果不是就获取属性interface的值;如果generatedBeanName继续为空,就获取实例Class的名称,将id设置为generatedBeanName的值,判断Spring上下文中是否存在相同的id,如果存在,就通过添加常数的方式获得新的id,然后保存在Spring上下文和RootBeanDefinition中。如果Spring上下文中存在相同的id,会抛出异常;
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);
}
- 依次看看<dubbo:protocol>、<dubbo:service>、<dubbo:provider>和<dubbo:consumer>是如何解析标签的。在Spring上下文中获取BeanDefinition,并遍历,如果获取的属性protocol的值是ProtocolConfig类,并且获取的name值与上面的id一致,就保存到BeanDefinition中;如果<dubbo:service>配置了class属性,那么为具体class配置的类注册Bean,并注入ref属性;
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);
}
- 获取<dubbo:service>标签的<property>子标签,支持<property name="" ref="" /> or <property name="" value=""/>两种形式,并注入到BeanDifinition中
private static void parseProperties(NodeList nodeList, RootBeanDefinition beanDefinition) {
if (nodeList != null && nodeList.getLength() > 0) {
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node instanceof Element) {
if ("property".equals(node.getNodeName())
|| "property".equals(node.getLocalName())) {
String name = ((Element) node).getAttribute("name");
if (name != null && name.length() > 0) {
String value = ((Element) node).getAttribute("value");
String ref = ((Element) node).getAttribute("ref");
if (value != null && value.length() > 0) {
beanDefinition.getPropertyValues().addPropertyValue(name, value);
} else if (ref != null && ref.length() > 0) {
beanDefinition.getPropertyValues().addPropertyValue(name, new RuntimeBeanReference(ref));
} else {
throw new UnsupportedOperationException("Unsupported <property name=\"" + name + "\"> sub tag, Only supported <property name=\"" + name + "\" ref=\"...\" /> or <property name=\"" + name + "\" value=\"...\" />");
}
}
}
}
}
}
}
- <dubbo:provider>标签可以嵌套<dubbo:service>,<dubbo:consumer>标签可以嵌套<dubbo:reference>,解析内部的service生成bean的时候,会把外层的provider实例对象注入service,这种设计方式允许内部标签直接获取外部标签属性。
private static void parseNested(Element element, ParserContext parserContext, Class<?> beanClass, boolean required, String tag, String property, String ref, BeanDefinition beanDefinition) {
NodeList nodeList = element.getChildNodes();
if (nodeList != null && nodeList.getLength() > 0) {
boolean first = true;
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node instanceof Element) {
if (tag.equals(node.getNodeName())
|| tag.equals(node.getLocalName())) {
if (first) {
first = false;
String isDefault = element.getAttribute("default");
if (isDefault == null || isDefault.length() == 0) {
beanDefinition.getPropertyValues().addPropertyValue("default", "false");
}
}
BeanDefinition subDefinition = parse((Element) node, parserContext, beanClass, required);
if (subDefinition != null && ref != null && ref.length() > 0) {
subDefinition.getPropertyValues().addPropertyValue(property, new RuntimeBeanReference(ref));
}
}
}
}
}
}
- 循环遍历(3)中得到的每个解析标签实现类的方法,判断是不是公共的以set为前缀的方法且只有一个入参,如果是,获取对应的入参类型,方法名称和属性名,如public void setName(String name),则入参类型是java.lang.String,方法名称是setName,去掉set,然后首字母小写获得的就是属性名name。
- 再通过is+方法名称(去掉set)或者get+方法名称(去掉set)获得一个getter方法,如果这个getter方法不为空,且是公共的,并且getter方法的返回类型等于setter方法的入参类型,就继续下一步判断,否则continue跳出当前循环。
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;
}
//省略
}
}
- 如果属性名是parameters,就进行解析,将获取的每个parameter节点的key和value保存到一个Map集合中,注意:如果parameter的属性hide的值为true,需要在key前面加一个隐藏前缀才能保存在集合中。当属性名是methods,arguments时同理;
private static ManagedMap parseParameters(NodeList nodeList, RootBeanDefinition beanDefinition) {
if (nodeList != null && nodeList.getLength() > 0) {
ManagedMap parameters = null;
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node instanceof Element) {
if ("parameter".equals(node.getNodeName())
|| "parameter".equals(node.getLocalName())) {
if (parameters == null) {
parameters = new ManagedMap();
}
String key = ((Element) node).getAttribute("key");
String value = ((Element) node).getAttribute("value");
boolean hide = "true".equals(((Element) node).getAttribute("hide"));
if (hide) {
key = Constants.HIDE_KEY_PREFIX + key;
}
parameters.put(key, new TypedStringValue(value, String.class));
}
}
}
return parameters;
}
return null;
}
- 获取配置文件xml标签节点属性的值value,如:获取<dubbo:application name="hello-world-provider" />这个标签的属性name的值,如果值不为空,去掉空格长度大于0,把(属性名,值)保存在RootBeanDefinition类中。
参考
https://blog.csdn.net/qq_2763...
《深入理解Apache Dubbo与实战》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。