Dubbo和Spring结合使用了大量的自定义标签。dubbo正是通过这些标签和Spring融合在一起,实现了服务暴露、服务引用、协议声明、注册中心配置等功能。
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- provider's application name, used for tracing dependency relationship -->
<dubbo:application name="demo-provider"/>
<!--<dubbo:monitor protocol="registry"></dubbo:monitor>-->
<!-- use multicast registry center to export service -->
<!--<dubbo:registry address="multicast://224.5.6.7:1234"/>-->
<!--订阅并注册在zk上-->
<dubbo:registry id="vmZk" protocol="zookeeper" address="192.168.0.100:2181" check="false" />
<!-- use dubbo protocol to export service on port 20880 -->
<!--配置线程模式-->
<dubbo:protocol id="dubbo1" name="dubbo" port="20881" dispatcher="execution" threadpool="cached" threads="5"/>
<dubbo:protocol id="dubbo2" name="dubbo" port="20882" dispatcher="execution" threadpool="cached" threads="5"/>
<!-- service implementation, as same as regular local bean -->
<bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>
<!-- declare the service interface to be exported -->
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" protocol="dubbo1" registry="vmZk"/>
</beans>
dubbo的命名空间为xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
,点击这个链接就可以找到dubbo.xsd,同目录下还有spring.handlers、spring.schemas。
spring.handlers
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
通过spring.handlers的内容可以发现dubbo的命名空间处理类为DubboNamespaceHandler
,在DubboNamespaceHandler中注册的了很多解析器,它们都实现了BeanDefinitionParser接口。当spring容器解析到对应的标签时就会回调BeanDefinitionParser.parse
交给用户处理。
spring自定义标签
package com.alibaba.dubbo.config.spring.schema;
/**
* DubboNamespaceHandler
*
* @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 AnnotationBeanDefinitionParser());
}
}
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />
可以配置的属性
<xsd:element name="service" type="serviceType">
<xsd:annotation>
<xsd:documentation><![CDATA[ Export service config ]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:complexType name="serviceType">
<xsd:complexContent>
<xsd:extension base="abstractServiceType">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element ref="method" minOccurs="0" maxOccurs="unbounded" />
<xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />
<xsd:element ref="beans:property" minOccurs="0" maxOccurs="unbounded" />
</xsd:choice>
<xsd:attribute name="interface" type="xsd:token" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[ Defines the interface to advertise for this service in the service registry. ]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:expected-type type="java.lang.Class"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ref" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ The service implementation instance bean id. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
启动方式一
直接启动spring容器,读取dubbo的配置
public class Provider {
public static void main(String[] args) throws Exception {
//Prevent to get IPV6 address,this way only work in debug mode
//But you can pass use -Djava.net.preferIPv4Stack=true,then it work well whether in debug mode or not
System.setProperty("java.net.preferIPv4Stack", "true");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
context.start();
System.in.read(); // press any key to exit
}
}
启动方式二
dubbo提供了容器类读取配置文件来启动spring容器,然后在spring容器启动后spring自动加载applicationContext.xml,并解析dubbo标签生成dubbo各种需要的类,并在服务关闭时候关闭spring 容器
- 配置文件中配置启动容器
dubbo.container=log4j,spring
- 调dubbo的main方法
package com.alibaba.dubbo.demo.provider;
import com.alibaba.dubbo.container.Main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Provider {
public static void main(String[] args) throws Exception {
Main.main(args);
}
}
- 加载配置并启动所有容器
public static final String CONTAINER_KEY = "dubbo.container";
public static void main(String[] args) {
try {
if (args == null || args.length == 0) {
//读取配置文件
String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
args = Constants.COMMA_SPLIT_PATTERN.split(config);
}
final List<Container> containers = new ArrayList<Container>();
for (int i = 0; i < args.length; i ++) {
//获取扩展点实例log4j,spring-SpringContainer.class
containers.add(loader.getExtension(args[i]));
}
logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce.");
if ("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY))) {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
for (Container container : containers) {
try {
//停止容器
container.stop();
logger.info("Dubbo " + container.getClass().getSimpleName() + " stopped!");
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
synchronized (Main.class) {
//停止主线程
running = false;
Main.class.notify();
}
}
}
});
}
for (Container container : containers) {
//启动容器
container.start();
logger.info("Dubbo " + container.getClass().getSimpleName() + " started!");
}
System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " Dubbo service server started!");
} catch (RuntimeException e) {
e.printStackTrace();
logger.error(e.getMessage(), e);
System.exit(1);
}
synchronized (Main.class) {
while (running) {
try {
//主线程等待,程序进程不会退出
Main.class.wait();
} catch (Throwable e) {
}
}
}
}
......
}
- 加载spring的配置并启动,而且SpringContainer实现了stop方法,该方法会在关机时调用;dubbo的优雅看关机请参照https://www.jianshu.com/p/aa22eac09d8c
public class SpringContainer implements Container {
public static final String SPRING_CONFIG = "dubbo.spring.config";
public static final String DEFAULT_SPRING_CONFIG = "classpath*:META-INF/spring/*.xml";
private static final Logger logger = LoggerFactory.getLogger(SpringContainer.class);
static ClassPathXmlApplicationContext context;
public static ClassPathXmlApplicationContext getContext() {
return context;
}
public void start() {
String configPath = ConfigUtils.getProperty(SPRING_CONFIG);
if (configPath == null || configPath.length() == 0) {
configPath = DEFAULT_SPRING_CONFIG;
}
context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+"));
context.start();
}
public void stop() {
try {
if (context != null) {
context.stop();
context.close();
context = null;
}
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
}
网友评论