1、Dubbo消费者调用服务提供者例子
本专栏分析的Dubbo源码是基于2.6.x版本
public class Consumer {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-consumer.xml"});
context.start();
DemoService demoService = (DemoService) context.getBean("demoService");
while (true) {
try {
Thread.sleep(1000);
String hello = demoService.sayHello("world");
System.out.println(hello);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/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://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"/>
<dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"/>
</beans>
public class Provider {
public static void main(String[] args) throws Exception {
System.setProperty("java.net.preferIPv4Stack", "true");
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
context.start();
System.in.read();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/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://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="demo-provider"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<dubbo:protocol name="dubbo" port="20880"/>
<bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>
</beans>
2、Spring解析dubbo配置文件
先启动服务提供者,再启动消费者,发现控制台可以正常输出。下面分析一下Spring是如何解析dubbo的消费者和服务提供者的配置文件。Spring容器提供了IOC功能,可以替我们生成bean。通常,我们将bean的定义放在xml文件中,我们来分析一下Spring加载xml配置文件并生成bean过程。Spring提供的容器分为两种:BeanFactory和ApplicationContext
。其中BeanFactory是懒加载,也就是延迟初始化,它在你调用getBean时才会初始化这个bean,而ApplicationContext是初始化容器时就会加载非延迟初始化的bean
。先简单概况下Spring容器生成bean的过程,首先通过loadBeanDefinition过程将bean的信息封装成一个个BeanDefinition,然后再根据这些BeanDefinition创建bean。下面看Spring解析Dubbo的配置文件并生成bean的过程。
//1、new ClassPathXmlApplicationContext时Spring容器初始化,此时会先调用loadBeanDefinition方法去加载
//解析xml配置文件
context =
new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
//2、加载配置文件最终会走到这里
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
// 3、这里其实已经通过dom4j将xml文件解析成了Document,将xml中的一项一项配置解析成了一个个
//Node去读取处理.
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
// 4、判断是否是Spring默认可以处理的Node.这里看下面截图,由于dubbo:application
//是dubbo中定义的,不属于Spring的命名空间管理
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
//http://dubbo.apache.org/schema/dubbo
String namespaceUri = getNamespaceURI(ele);
//DubboNameSpaceHandler
NamespaceHandler handler =
this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
private static BeanDefinition parse(Element element, ParserContext parserContext,
Class<?> beanClass, boolean required) {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
//class com.alibaba.dubbo.config.ApplicationConfig
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);
//解析id属性
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);
}
//注册BeanDefinition
parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
//将id属性放入beanDefinition中,后续getBean创建bean时就是根据这些属性来创建bean,
//这里创建的bean是ApplicationConfig
beanDefinition.getPropertyValues().addPropertyValue("id", id);
}
//删去一些代码,reference是解析得到的value值,可见这里将属性和属性值都放入了BeanDefinition
beanDefinition.getPropertyValues().addPropertyValue(property, reference);
return beanDefinition;
}
到这里就解析完了,Spring将xml中的application节点解析成一个BeanDefinition,并注册到Registry中,Registry就是一个Map。下面分析Spring创建这个ApplicationConfig的过程。
3、Spring创建ApplicationConfig
context =
new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
// Spring容器的初始化过程,new ClassPathXmlApplicationContext后会走到这里
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
// 这里面就会执行上面的分析过程,调用loadBeanDefinition解析BeanDefinition
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.可以看到Spring容器初始化
//的后面会初始化非延迟加载的bean,这里会走到下图的preInstantiasteSingletons方法
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
}
}
//Spring创建bean最终会走到这里
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd,
final Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//删除一些无用代码,这里会调用反射创建bean,创建完仅是一个空的bean,属性还没有赋值
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Object exposedObject = bean;
try {
//属性赋值,最终也是调用反射进行赋值
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
return exposedObject;
}
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
// 这里的pvs就是之前解析配置文件得到BeanDefinition时,给BeanDefinition注入进去的
PropertyValues pvs = mbd.getPropertyValues();
// 删除一些代码,最终这里会调用反射赋值,跳来跳去有点复杂
applyPropertyValues(beanName, mbd, bw, pvs);
}
protected void addSingleton(String beanName, Object singletonObject) {
//最终创建完bean以后会将它保存起来(猜测,Spring容器初始化以后,非懒加载的bean已经以如下方式
//保存到Spring容器中了,后续通过@Autowired注解)来获取时就是从这里面获取,只是分析,还没有看源码)
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName,
(singletonObject != null ? singletonObject : NULL_OBJECT));
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。