一、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容器的,如下:
不管是哪种方式来启动容器,他们都是spring在解析标签的时候遇到用户自定义的命名空间就交由用户自己定义的解析器来解析,然后都返回spring中通用的对象描述类BeanDefinition,然后spring就可以为其创建java bean对象了