本文使用的是dubbo-2.5.8版,
注册中心是zk(使用不同端口构建第一个伪集群)
配置文件: debbo-demo-provider.xml
<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-2.5.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="demo-provider"/>
<!-- 使用multicast广播注册中心暴露服务地址 -->
<!-- <dubbo:registry address="multicast://224.5.6.7:1234"/> -->
<dubbo:registry protocol="zookeeper" address="10.0.28.54:2181;10.0.28.54:2182;10.0.28.54:2183"/>
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- 和本地bean一样实现服务 -->
<bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>
</beans>
服务容器启动:在dubbo-demo-provider项目中
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
}
}
Main启动spring容器加载
META-INF/spring/dubbo-demo-provider.xml 进行初始化,设置上下文;
这个过程spring会读取classptsh:META-INF下的 spring.handler文件,加载解析 xsd文件的Handler:【DubboNamespaceHandler】解析xsd定义的节点;
我们看到DubboNamespaceHandler对service节点的解析为
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
下面来看看ServiceBean.java
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware {
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
SpringExtensionFactory.addApplicationContext(applicationContext);
if (applicationContext != null) {
SPRING_CONTEXT = applicationContext;
try {
Method method = applicationContext.getClass().getMethod("addApplicationListener", new Class<?>[]{ApplicationListener.class}); // 兼容Spring2.0.1
method.invoke(applicationContext, new Object[]{this});
supportedApplicationListener = true;
} catch (Throwable t) {
if (applicationContext instanceof AbstractApplicationContext) {
try {
Method method = AbstractApplicationContext.class.getDeclaredMethod("addListener", new Class<?>[]{ApplicationListener.class}); // 兼容Spring2.0.1
if (!method.isAccessible()) {
method.setAccessible(true);
}
method.invoke(applicationContext, new Object[]{this});
supportedApplicationListener = true;
} catch (Throwable t2) {
}
}
}
}
}
public void onApplicationEvent(ContextRefreshedEvent event) {
if (isDelay() && !isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}
private boolean isDelay() {
Integer delay = getDelay(); //==null
ProviderConfig provider = getProvider();
if (delay == null && provider != null) {
delay = provider.getDelay();
}
return supportedApplicationListener && (delay == null || delay == -1); //return ture;
}
}
ServiceBean分别实现了
ApplicationContextAware接口,因此在启动的时候spring会自动将上下文applicationContext 通过
setApplicationContext 方法将spring上下文注入到 ServiceBean中,以备后续使用;
InitializingBean.afterPropertiesSet() 方法会使用到 applicationContext afterProperties最后会判断
if (!isDelay()) {
export();
}
由处理逻辑可知,isDelay 返回ture,所以export()方法不是在 afterProperties方法处执行;
又 实现 ApplicationListener 接口,负责在初始化的时候会触发事件,并通过调用重载方法
onApplicationEvent 来进行初始化。因此,ServiceBean的export() 方法是在触发事件的时候才真正执行。
export()方法经过 doExport() , doExportUrls() ,doExportUrlsFor1Protocol ()后才真正的开始执行服务端相关逻辑。
其中 doExport()主要负责参数相关的校验;
doExportUrls()首先会
1.获取注册中心地址列表
List<URL> registryURLs = loadRegistries(true);
2.
根据注册中心地址列表和 协议【
protocols
】 对服务进行暴露。
由配置文件可知 protocols =[dubbo] 【
<dubbo:protocol name="dubbo" port="20880"/>
】