Dubbo和Spring的融合
通常,我們使用Dubbo的時(shí)候會(huì)創(chuàng)建如下的配置文件
<?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">
<!-- 提供方應(yīng)用信息,用于計(jì)算依賴關(guān)系 -->
<dubbo:application name="hello-world-app" />
<!-- 使用multicast廣播注冊(cè)中心暴露服務(wù)地址 -->
<dubbo:registry address="multicast://127.0.0.1:1234" />
<!-- 用dubbo協(xié)議在20880端口暴露服務(wù) -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 聲明需要暴露的服務(wù)接口 -->
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />
<!-- 和本地bean一樣實(shí)現(xiàn)服務(wù) -->
<bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />
</beans>
像dubbo:service這樣的標(biāo)簽是如何添加到spring并且被識(shí)別的呢?
答案是它使用了Spring的xsd schema文檔定義語(yǔ)言來(lái)實(shí)現(xiàn)的,首先我們自定義一套schema來(lái)熟悉一下Spring的可擴(kuò)展Schema。
分為五個(gè)步驟
1.設(shè)計(jì)配置屬性和Bean
2.編寫(xiě)xsd文件
3.使用NamespaceHandler和BeanDefinitionHandler來(lái)完成解析工作
4.配置spring.handlers和spring.schemas串聯(lián)這些類
5.在spring中使用這個(gè)bean
(1)定義一個(gè)User類:
@Data
public class User {
String id;
private String name;
private String email;
private int age;
}
(2)編寫(xiě)xsd文件
文件名稱為spring-test.xsd,名稱可以自定義
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.fanfte.com/schema/user"
xmlns:tns="http://www.fanfte.com/schema/user" elementFormDefault="qualified">
<element name="user">
<complexType>
<attribute name="id" type="string"/>
<attribute name="age" type="int" />
<attribute name="name" type="string" />
<attribute name="email" type="string" />
</complexType>
</element>
</schema>
(3)使用NamespaceHandler和BeanDefinitionHandler來(lái)完成解析工作
首先編寫(xiě)bean解析器
public class UserBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return User.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String id = element.getAttribute("id");
String name = element.getAttribute("name");
String email = element.getAttribute("email");
Integer age = Integer.parseInt(element.getAttribute("age"));
if(StringUtils.hasText(id)) {
builder.addPropertyValue("id", id);
}
if(StringUtils.hasText(name)) {
builder.addPropertyValue("name", name);
}
if(StringUtils.hasText(email)) {
builder.addPropertyValue("email", email);
}
if(age != null) {
builder.addPropertyValue("age", age);
}
}
}
再編寫(xiě)命名空間處理器:
public class UserNameSpaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
}
}
(4)配置spring.handlers和spring.schemas串聯(lián)這些類
再項(xiàng)目的classpath下的META-INF下配置兩個(gè)文件:
spring.handlers
http\://www.fanfte.com/schema/user=com.fanfte.rpc.spring.UserNameSpaceHandler
spring.schemas
http\://www.fanfte.com/schema/user.xsd=META-INF/spring-text.xsd
這里的路徑對(duì)應(yīng)了第二步xsd文件中的路徑,類名對(duì)應(yīng)了命名空間解析器的全路徑名com.fanfte.rpc.spring.UserNameSpaceHandler
(5)配置和加載bean到spring容器
classpath下新建一個(gè)application-tag.xml文件,內(nèi)容如下:
<?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:userTag="http://www.fanfte.com/schema/user"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.fanfte.com/schema/user http://www.fanfte.com/schema/user.xsd">
<userTag:user id="userTag" name="userBean" email="fanfte@163.com" age="18"/>
<!--<fanfteRPC:reference id="demoService" interface="com.fanfte.rpc.service.DemoService"></fanfteRPC:reference>-->
</beans>
xml中使用配置了的bean ,標(biāo)簽名稱為userTag,對(duì)應(yīng)了文件中的第四行的xmlns:userTag
最后,在main方法中使用這個(gè)bean
public class UserTagTest {
@Autowired
private DemoService demoService;
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:META-INF/application-tag.xml");
User user = (User) context.getBean("userTag");
System.out.println(user.getAge() + " " + user.getEmail() + " " + user.getName());
}
}
這樣就使用了自定義的xsd來(lái)配置使用我們自定義的標(biāo)簽。dubbo也有一套自己的自定義標(biāo)簽。
Dubbo融合Spring
<1>首先我們看到DubboNamespaceHandler這個(gè)類內(nèi)部
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
static {
Version.checkDuplicate(DubboNamespaceHandler.class);
}
@Override
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 AnnotationBeanDefinitionParser());
}
}
這個(gè)類就是Dubbo的命名空間處理器,可以看到有10種的命名空間,而定義的配置屬性也定義在了xxxConfig和xxxBean里面,而解析器則使用了DubboBeanDefinitionParser和AnnotationBeanDefinitionParser兩種。
從這個(gè)類可以看到已經(jīng)完成了dubbo自定義schema完成了上面的第(1)和(3)步工作
<2>第二部我們可以在dubbo.xsd文件種發(fā)現(xiàn)
<xsd:element name="service" type="serviceType">
<xsd:annotation>
<xsd:documentation><![CDATA[ Export service config ]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.apache.dubbo.config.ServiceConfig"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="reference" type="referenceType">
<xsd:annotation>
<xsd:documentation><![CDATA[ Reference service config ]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.apache.dubbo.config.ReferenceConfig"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
這里貼出來(lái)部分代碼,做了多種的xsd元素標(biāo)簽配置。
<3>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
<4>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
這兩個(gè)文件串聯(lián)了Dubbo的整個(gè)csd的解析
最后,在applicationContext這類xml文件中我們使用dubbo的標(biāo)簽讓spring管理我們定義的ServiceBean。
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />
總結(jié):
1.通過(guò)上述5個(gè)步驟,我們實(shí)現(xiàn)了自定義的標(biāo)簽
2.通過(guò)觀察Dubbo源碼中的邏輯和配置,發(fā)現(xiàn)Dubbo從原理上也是這么實(shí)現(xiàn)它的自定義標(biāo)簽的,對(duì)于Spring的融合能夠完美支持。其實(shí)也就是利用了Spring的特性。