Dubbo代码解析
dubbo发布服务端的源码解析。
dubbo发布服务不需要集成重量级的web服务器,直接提供了com.alibaba.dubbo.container.Main类进行
启动发布。
if (args == null || args.length == 0) {
String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
args = Constants.COMMA_SPLIT_PATTERN.split(config);
}
开始判断main函数的传入参数,在args参数为空的情况下,从部署环境中取得dubbo.container属性,
dubbo提供了configUtils的dubbo环境配置的访问工具类。
下面就是configUtils的取得属性的配置的顺序
String value = System.getProperty(key);
if (value != null && value.length() > 0) {
return value;
}
Properties properties = getProperties();
return replaceProperty(properties.getProperty(key, defaultValue), (Map)properties);
先是从取得系统属性,如果取得有效配置值则直接采用,否则去构造配置对象,从配置对象中取得配置值,
下面是构造配置对象的代码:
private static volatile Properties PROPERTIES;
public static Properties getProperties() {
if (PROPERTIES == null) {
synchronized (ConfigUtils.class) {
if (PROPERTIES == null) {
String path = System.getProperty(Constants.DUBBO_PROPERTIES_KEY);
if (path == null || path.length() == 0) {
path = System.getenv(Constants.DUBBO_PROPERTIES_KEY);
if (path == null || path.length() == 0) {
path = Constants.DEFAULT_DUBBO_PROPERTIES;
}
}
PROPERTIES = ConfigUtils.loadProperties(path, false, true);
}
}
}
return PROPERTIES;
}
采用懒加载的模式,进行构造,为了保证这段代码能在多线程下执行,运用了严格加锁的机制。先是通过寻求系统属性值dubbo.properties.file,如果存在,直接加载以此作为路径加载文件。没有设置的话就采用默认的配置文件dubbo.properties。然后就会通过先直接搜索文件系统,类路径等方式,来取得系统的配置。当然整个配置文件默认的可以没有,而且只有一个文件才会起作用。
然后从属性环境中读取到dubbo.container值,得到需要以命名方式加载哪些container。
final List<Container> containers = new ArrayList<Container>();
for (int i = 0; i < args.length; i ++) {
containers.add(loader.getExtension(args[i]));
}
Container是接口。
在dubbo中有很多被spi注释的接口。这些都是具有很强扩展能力的地方。
@SPI("spring")
public interface Container {
/**
* start.
*/
void start();
/**
* stop.
*/
void stop();
}
每个spi注解的实现类都是有扩展点加载器(ExtensionLoader)进行加载,
在main类中,直接定义了一个静态变量:
private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
在加载Main类的时候,就会以Container类为参数直接加载。
下面看看getExtensionLoader函数。
在这个函数中先做Container中做有效性验证,而且判断是否被spi注解注释。进一步确认了扩展点加载器只能加载被spi注解的类。
在ExtensionLoader类中存在
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
将spi接口类和对应的加载器一一对应起来,同时提供懒加载的缓存作用。
下面是extensionloader的简单类图:
其中extensionFactory是扩展点是一个工厂类,因为扩展点实现如果依赖其他的类的话,只要,在里面依赖的对象遵从javabean方式,那么dubbo就会在环境对象池中(这里主要是spring容器以及扩展点工厂中取得依赖的对象)。
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
ExtensionFactory本身就是spi扩展点类,所以它也需要ExtensionLoader进行加载。
ExtensionLoader这里用了一种变异的单例模式,因为ExtensionLoader对于每个扩展点只有一个。
ExtensionFactory的扩展实现,是通过这个spi接口对应的ExtensionLoader的对应的getAdaptiveExtension得到。
在getAdaptiveExtension这个方法中,先是看是否已经创建,有直接返回,
没有的话
采用同步锁的方式去调用createAdaptiveExtension创建这个转接器对象。
下面是createAdaptiveExtension的调用序列图:
就是在createAdaptiveExtension中如果发现扩展点的实现类没有被加载,就会调用getExtensionClasses从三个目录中去
META-INF/dubbo/internal/,META-INF/dubbo/,META-INF/services/中去加载接口全称命名的配置文件。
配置文件记录的就是实现类以及该类对应的注册名。(当然也可以不提供注册名,dubbo会直接按照通过注解Extension命名,以及直接使用简单类名)
加载配置文件方法loadFile中,对于每个扩展点的实现类,他都会查看这个实现类是否被Adaptive标注。当然这个函数中有些原则性的错误检测,比喻一个扩展点只能只有一个Adaptive扩展点。
其实对于一个扩展点,声明为Adaptive的往往是其他实现的集成。
所以一个扩展点在dubbo中为默认存在三种实现,Adaptive,wrapped,以及normal实现。主要提供特定功能,主要是normal实现,当然wrapped的可以做响应过滤权限验证等类似aop的切面功能。Adaptive则某种意义上是集成。
对于扩展点实现类还支持Activate注解,这样就指明了这个扩展实现被激活的加载条件。
如果存在直接被Adaptive注解的类,这直接使用这个类。如果没有的话就动态创建java文件然后利用java编译器去运行时编译产生这个类。
最后创建完extension实现后,在方法injectExtension中实现依赖注入。