spring boot是如何解析dubbo 配置文件的标签的?
2 个回答
用眼睛是看不到事物的本质的,只有心灵才能够洞察一切。hello这里debug的代码用的是github上dubbo项目的dubbo-demo里的 dubbo-demo-xml下的代码。 spring的xml文件里自定义标签的解析是基于spring.handlers文件指定的解析类来解析的,dubbo标签是在 dubbo-config-spring的jar包下的 META-INF/spring.handlers文件里。这里先看下一个consumer端的配置文件内容: ``` <beans // xmlns:xsi是xsi标签命名空间 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" // xmlns:dubbo是dubbo标签的命名空间 xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" // 当前那xml文件默认的命名空间 xmlns="http://www.springframework.org/schema/beans" // xsi:schemaLocation 配置了每个命名空间对应里配置规范,用来做格式校验 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="demo-consumer"/> <!-- 注册中心配置--> <dubbo:registry address="zookeeper://127.0.0.1:2181" timeout="6000"/> <!-- 协议配置--> <dubbo:protocol name="dubbo"/> <!-- provider配置--> <bean id="providerService" class="org.apache.dubbo.demo.provider.ProviderServiceImpl"/> <dubbo:service interface="org.apache.dubbo.demo.provider.ProviderServiceImpl" ref="providerService" timeout="60000"/> <!-- consumer配置--> <dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService" timeout="6000" /> </beans> ``` xmlns是指定命名空间,这里可以看到dubbo的命名空间配置是xmlns:dubbo=“http://dubbo.apache.org/schema/dubbo” ### 1、根据dubbo标签的命名空间获取解析类 根据dubbo标签的命名空间找到META-INF/spring.handlers里的解析类:  这个匹配过程如图:  init()方法负责注册每个标签对应的具体Parser类: ``` 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("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true)); this.registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true)); this.registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); this.registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true)); this.registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.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)); // provider端配置 this.registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); // consumer端配置 this.registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); this.registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser()); } ``` 可以看到这里包含了dubbo标签的所有种类的解析类,比如上面配置文件里的dubbo:application、dubbo:registry、dubbo:reference注解的解析器 ### 2、将配置解析成Beanfinition并注册到BeanFactory过程 核心逻辑在DubboBeanDefinitionParser类的parse方法里,先看DubboBeanDefinitionParser类的构造器: ``` public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) { this.beanClass = beanClass; this.required = required; } ``` beanClass就是每个配置所对应的类型,由上面的init()方法传入,比如dubbo:reference对应ReferenceBean.class parse方法代码如下,由于总的代码比较长,我这里只贴出主要代码: ``` RootBeanDefinition beanDefinition = new RootBeanDefinition(); // 设置类型,spring容器在实例化beanDefinition时,会反射这个类的构造器实例出bean对象 beanDefinition.setBeanClass(beanClass); ... // 将BeanDefinition注册到容器里,这个id会被作为beanName属性存在BeanDefinition里 // registerBeanDefinition的方法签名是void registerBeanDefinition(String beanName, BeanDefinition beanDefinition),所以id是作为beanName传入的 throws BeanDefinitionStoreException; if (StringUtils.isNotEmpty(id)) { parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); beanDefinition.getPropertyValues().addPropertyValue("id", id); } ... // 遍历配置里的所有属性,比如dubbo:reference配置里的id、interface等属性,放到BeanDefinition里 NamedNodeMap attributes = element.getAttributes(); len = attributes.getLength(); for(i = 0; i < len; ++i) { Node node = attributes.item(i); name = node.getLocalName(); if (!props.contains(name)) { if (parameters == null) { parameters = new ManagedMap(); } String value = node.getNodeValue(); parameters.put(name, new TypedStringValue(value, String.class)); } } if (parameters != null) { beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters); } ``` 主要是三步: 1. 设置BeanDefinition的beanClass属性,beanClass由上面的init()方法传入,比如:dubbo:reference对应ReferenceBean.class,spring容器在实例化beanDefinition时,会反射这个类的构造器实例出bean对象 2. 通过parse方法的parserContext获取对应bean的BeanDefinitionRegistry,将BeanDefinition注册到BeanFactory里,这里有个关键点是传入的id会被作为beanName设置到BeanDefinition里,而在依赖注入时,会以beanName为key来找bean 3. 解析出配置的属性名字,比如consumer配置里的id、interface、group、version等信息,并将这些配置放到BeanDifinition的propertyValueList里(key为parameters),供后面实例化用。 到这里已经解析成BeanDefinition并注册到容器中,下一步就是Spring容器实例化BeanDefinition。后面会单独讲provider和consumer端怎么被实例化。编辑于 2022-03-10 19:58 •IP属地 甘肃举报评论收藏
醉后不知天在水,满船清梦压星河。李后才1. 要了解Dubbo是如何解析标签的,首先要清楚一点就是Spring如何处理自定义标签的,因为Dubbo的标签可以算是Spring自定义标签的一种情况; 2. Spring通过两个接口来解析自定义的标签:NamespaceHandler和BeanDefinitionParser接口;NamespaceHandler负责namespace的处理,而BeanDefinitionParser负责Bean的解析; 3. 我们自定义标签的时候,可以实现NamespaceHandler接口,但更多的是继承NamespaceHandler的抽象类:NamespaceHandlerSupport,然后在classpath的META-INF目录下编写一个spring.handlers文件,在该文件中定义namespace的URL和namespace处理类的映射。 比如Dubbo的spring.handlers文件内容(/是转义): ``` http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler ``` 4. spring框架初始化时会加载所有classpath的spring.handlers文件,把namespace的URL和namespace处理类映射到Map中,Spring在解析的时候,遇到一些自定义的标签的时候,会在这个Map中查找namespace处理类,使用这个使用这个自定义的处理类来进行标签的解析工作; 5. 同样,Dubbo框架的namespace的处理类是DubboNamespaceHandler,也是实现了NamespaceHandlerSupport接口来处理命名空间,然后在这个类的初始化的时候给所有标签都注册了各自的解析器;而Dubbo的解析类DubboBeanDefinitionParser同样也是实现了BeanDefinitionParser接口; DubboNamespaceHandler 源代码如此下: ```java /** * DubboNamespaceHandler * * @author william.liangf * @export */ public class DubboNamespaceHandler extends NamespaceHandlerSupport { static { Version.checkDuplicate(DubboNamespaceHandler.class); } public void init() { 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 DubboBeanDefinitionParser(AnnotationBean.class, true)); } } ``` 可以看到,Dubbo有10个标签,每个标签会解析到对应的实体上,每个实体中的属性对应标签中的属性,比如ApplicationConfig类: ```java /** * ApplicationConfig * * @author william.liangf * @export */ public class ApplicationConfig extends AbstractConfig { private static final long serialVersionUID = 5508512956753757169L; // 应用名称 private String name; // 模块版本 private String version; // 应用负责人 private String owner; // 组织名(BU或部门) private String organization; // 分层 private String architecture; // 环境,如:dev/test/run private String environment; // Java代码编译器 private String compiler; // 日志输出方式 private String logger; // 注册中心 private List<RegistryConfig> registries; // 服务监控 private MonitorConfig monitor; // 是否为缺省 private Boolean isDefault; public ApplicationConfig() { } public ApplicationConfig(String name) { setName(name); } @Parameter(key = Constants.APPLICATION_KEY, required = true) public String getName() { return name; } public void setName(String name) { checkName("name", name); this.name = name; if (id == null || id.length() == 0) { id = name; } } @Parameter(key = "application.version") public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getOwner() { return owner; } public void setOwner(String owner) { checkMultiName("owner", owner); this.owner = owner; } public String getOrganization() { return organization; } public void setOrganization(String organization) { checkName("organization", organization); this.organization = organization; } public String getArchitecture() { return architecture; } public void setArchitecture(String architecture) { checkName("architecture", architecture); this.architecture = architecture; } public String getEnvironment() { return environment; } public void setEnvironment(String environment) { checkName("environment", environment); if(environment != null) { if (! ("develop".equals(environment) || "test".equals(environment) || "product".equals(environment))) { throw new IllegalStateException("Unsupported environment: " + environment + ", only support develop/test/product, default is product."); } } this.environment = environment; } public RegistryConfig getRegistry() { return registries == null || registries.size() == 0 ? null : registries.get(0); } public void setRegistry(RegistryConfig registry) { List<RegistryConfig> registries = new ArrayList<RegistryConfig>(1); registries.add(registry); this.registries = registries; } public List<RegistryConfig> getRegistries() { return registries; } @SuppressWarnings({ "unchecked" }) public void setRegistries(List<? extends RegistryConfig> registries) { this.registries = (List<RegistryConfig>)registries; } public MonitorConfig getMonitor() { return monitor; } public void setMonitor(MonitorConfig monitor) { this.monitor = monitor; } public void setMonitor(String monitor) { this.monitor = new MonitorConfig(monitor); } public String getCompiler() { return compiler; } public void setCompiler(String compiler) { this.compiler = compiler; AdaptiveCompiler.setDefaultCompiler(compiler); } public String getLogger() { return logger; } public void setLogger(String logger) { this.logger = logger; LoggerFactory.setLoggerAdapter(logger); } public Boolean isDefault() { return isDefault; } public void setDefault(Boolean isDefault) { this.isDefault = isDefault; } } ```发布于 2022-03-07 10:47 •IP属地 浙江举报评论收藏
公众号进入网站体验更佳

