02. dubbo的Spring拓展原理
一、spring如何解析使用者自定義的標籤
總結起來就是,spring在啟動的時候會掃描所有依賴的jar包下 META-INFO目錄下的spring.schemas、spring.handlers這兩個檔案,然後根據裡面配置的處理器來呼叫使用者自定義的處理器來解析使用者自定義的標籤,至於spring為什麼會這樣做,就需要去了解spring的那套機制了,可通過《Spring原始碼深度解析》這本書來了解。
二、Spring Schema的實現原理(摘選自《Spring原始碼深度解析》--郝佳2013版)
三、dubbo實現Spring Schema拓展
上面知道了spring是怎麼實現xml拓展的原理之後,現在看看dubbo是怎麼實現spring的xml schema拓展的,用winrar開啟dubbo的jar包
上面的兩個檔案都出現了兩個配置,一個是dubbo.apache.xxx,另一個是code.alibabatech.xxx,這個其實是因為dubbo最開始是阿里巴巴的產品,後來開源之後貢獻給了apache基金會,出現兩個是過渡期的相容處理而已,以後都是會用dubbo.apache.xxx為準。
我們開啟com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler可以看到其原始碼如下:
package com.alibaba.dubbo.config.spring.schema;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import com.alibaba.dubbo.common.Version;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ConsumerConfig;
import com.alibaba.dubbo.config.ModuleConfig;
import com.alibaba.dubbo.config.MonitorConfig;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.ProviderConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.spring.AnnotationBean;
import com.alibaba.dubbo.config.spring.ReferenceBean;
import com.alibaba.dubbo.config.spring.ServiceBean;
/**
* 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:application>標籤的時候就會交給DubboBeanDefinitionParser去解析,並且相應的配置類是ApplicationConfig類,然後spring根據DubboBeanDefinitionParser解析返回的BeanDefinition物件來例項化物件
四、dubbo是怎麼啟動spring容器的
如果我們是dubbo提供com.alibaba.dubbo.container.Main.main這個方法來啟動我們的應用,可以看看這個main方法的程式碼如下:
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 ++) {
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) {
}
}
}
}
可以看到,這個方法裡面,首先是通過loader.getExtension(....)方法取得所有的Container,然後再通過for迴圈去呼叫每個Container的start方法,在dubbo中是使用com.alibaba.dubbo.container.spring.SpringContainer這個類來作為Container的,其start方法如下:
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();
}
可以看到,它裡面其實也是呼叫了org.springframework.context.support.ClassPathXmlApplicationContext.start()方法,之後的工作就交由spring去處理了,當然,如果我們不使用dubbo提供的Main方法,也完全可以像常規的spring專案一樣來開啟spring容器的,如下:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("配置檔案地址"); context.start();。 |
不管是哪種方式來啟動容器,他們都是spring在解析標籤的時候遇到使用者自定義的名稱空間就交由使用者自己定義的解析器來解析,然後都返回spring中通用的物件描述類BeanDefinition,然後spring就可以為其建立java bean物件了