dubbo 源码学习笔记 (一) —— ExtensionLoader和URL

欢迎访问我的个人博客休息的风

学习dubbo,我认为可以从最基本的ExtensionLoader和URL这两个类入手。

据官方介绍,dubbo框架的基本设计原则为:

  • 采用 Microkernel + Plugin 模式,Microkernel 只负责组装 Plugin,Dubbo 自身的功能也是通过扩展点实现的,也就是 Dubbo 的所有功能点都可被用户自定义扩展所替换。
  • 采用 URL 作为配置信息的统一格式,所有扩展点都通过传递 URL 携带配置信息
简单来说,就是dubbo的核心实现,都是通过ExtensionLoader加载SPI扩展类实现的。并且将各种配置最终都统一转为URL实例,或进行zk注册,或用于protocol生成的invoker中;
这里补充一下java的spi机制,

java spi的具体约定为:当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。 基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。jdk提供服务实现查找的一个工具类:java.util.ServiceLoader。

就是通过ServiceLoader加载位于META-INF/services/配置文件,找到具体的实现类名,并装载实例化。而dubbo的ExtensionLoader会加载META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/这三个路径配置的类。

ExtensionLoader的整个过程如下图:(图片有点小,请点击新的页签进行查看

dubbo 源码学习笔记 (一) —— ExtensionLoader和URL_第1张图片

具体看下ExtensionLoader的实现,首先ExtensionLoader位于dubbo-common工程的extension包中。

ExtensionLoader的基本方法getExtensionLoader实现为:

@SuppressWarnings("unchecked")
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    if (type == null)
        throw new IllegalArgumentException("Extension type == null");
    if (!type.isInterface()) {
        throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
    }
    //只能有@SPI注解的接口才能被加载
    if (!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException("Extension type(" + type +
                ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
    }
    //一个type类型对应一个ExtensionLoader实例,并缓存
    ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    if (loader == null) {
        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
        loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    }
    return loader;
}
在new ExtensionLoader时,如果type是ExtensionFactory,会通过getAdaptiveExtension方法,设置适配扩展工厂类;

private ExtensionLoader(Class type) {
    this.type = type;
    objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
getAdaptiveExtension这个方法中,通过createAdaptiveExtension()创建实例并缓存在cachedAdaptiveInstance中。
@SuppressWarnings("unchecked")
public T getAdaptiveExtension() {//以DCL方式获取实例,并缓存到cacheAdaptiveInstance中
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {
        if (createAdaptiveInstanceError == null) {
            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        instance = createAdaptiveExtension();
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                    }
                }
            }
        } else {
            throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
        }
    }

    return (T) instance;
}
创建前需要先获取相应的class
@SuppressWarnings("unchecked")
private T createAdaptiveExtension() {
    try {
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
    }
}
private Class getAdaptiveExtensionClass() {
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
getAdaptiveExtensionClass这个方法比较复杂,会先调用getExtensionClasses去加载之前说的META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/三个目录下,去找以此传入的type类型对应的配置文件,这个配置文件里面的类如果有@Adaptive注解,则加载并直接赋值给cacheAdaptiveClass静态变量;如果配置文件下的类都没有@Adaptive注解,则调用createAdatpiveExtensionClass去生成适配扩展工厂类的代码,并编译加载到内存。

ExtensionLoader 生成的自适应扩展点类如下:(Protocol, ProxyFactory等都是用以下方式生成代码)

package <扩展点接口所在包>;

public class <扩展点接口名>$Adpative implements <扩展点接口> {
    public <有@Adaptive注解的接口方法>(<方法参数>) {
        if(是否有URL类型方法参数?) 使用该URL参数
        else if(是否有方法类型上有URL属性) 使用该URL属性
        # 

        if(获取的URL == null) {
            throw new IllegalArgumentException("url == null");
        }

        根据@Adaptive注解上声明的Key的顺序,从URL获致Value,作为实际扩展点名。
        如URL没有Value,则使用缺省扩展点实现。如没有扩展点, throw new IllegalStateException("Fail to get extension");

        在扩展点实现调用该方法,并返回结果。
    }

    public <有@Adaptive注解的接口方法>(<方法参数>) {
        throw new UnsupportedOperationException("is not adaptive method!");
    }
}
以生成代码的方式,加载的类也会被缓存到cacheAdaptiveClass静态变量中,通过injectExtension方法对其实例属性进行赋值。
private T injectExtension(T instance) {
    try {
        if (objectFactory != null) {
            for (Method method : instance.getClass().getMethods()) {
                //以set开头的方法,利用该方法对实例属性进行赋值
                if (method.getName().startsWith("set")
                        && method.getParameterTypes().length == 1
                        && Modifier.isPublic(method.getModifiers())) {
                    Class pt = method.getParameterTypes()[0];
                    try {
                        String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                        //objectFactory主要有两个SpiExtensionFactory和SpringExtensionFactory,
                        //获取方法参数需要的类型的实例,调用set方法进行赋值
                        //SpiExtensionFactory识别pt,也就是类型
                        //SpringExtensionFactory识别property,也就是名称
                        Object object = objectFactory.getExtension(pt, property);
                        if (object != null) {
                            method.invoke(instance, object);
                        }
                    } catch (Exception e) {
                        logger.error("fail to inject via method " + method.getName()
                                + " of interface " + type.getName() + ": " + e.getMessage(), e);
                    }
                }
            }
        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
    return instance;
}
跟spring容器依赖注入相似,采用类的set方法进行属性赋值。至此,一个new ExtensionLoader才算实现完成。
接下来看下主要的方法getExtension,这个方法会用到createExtension和loadExtensionClasses。
// 此方法已经getExtensionClasses方法同步过。
private Map, Class> loadExtensionClasses() {
    //type是否有SPI注解
    final SPI defaultAnnotation = type.getAnnotation(SPI.class);
    if (defaultAnnotation != null) {
        //SPI注解value值为默认的extension,并且只能有一个
        String value = defaultAnnotation.value();
        if (value != null && (value = value.trim()).length() > 0) {
            String[] names = NAME_SEPARATOR.split(value);
            if (names.length > 1) {
                throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                        + ": " + Arrays.toString(names));
            }
            if (names.length == 1) cachedDefaultName = names[0];
        }
    }

    Map, Class> extensionClasses = new HashMap, Class>();
    //分别加载META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/三个目录下的spi
    loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
    loadFile(extensionClasses, DUBBO_DIRECTORY);
    loadFile(extensionClasses, SERVICES_DIRECTORY);
    return extensionClasses;
}
这三个文件具体的解析加载过程在loadFile中。

private void loadFile(Map, Class> extensionClasses, String dir) {
        //获取文件名
        String fileName = dir + type.getName();
        try {
            //获取文件路径
            Enumeration urls;
            ClassLoader classLoader = findClassLoader();
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
            if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL url = urls.nextElement();
try {
//读取每一个文件
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
try {
String line = null;
while ((line = reader.readLine()) != null) {
//忽略配置文件里的#号
final int ci = line.indexOf('#');
if (ci >= 0) line = line.substring(0, ci);
line = line.trim();
if (line.length() > 0) {
try {
String name = null;
//按=号进行切割,前部分为名称,后部分为类全路径名
int i = line.indexOf('=');
if (i > 0) {
    name = line.substring(0, i).trim();
    line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
    Class clazz = Class.forName(line, true, classLoader);
    //是否和type是同一个接口或是同一个类的子类
    if (!type.isAssignableFrom(clazz)) {
        throw new IllegalStateException("Error when load extension class(interface: " +
                type + ", class line: " + clazz.getName() + "), class "
                + clazz.getName() + "is not subtype of interface.");
    }
    //类有Adaptive注解,就是在getAdaptiveExtensionClass方法要返回的类
    if (clazz.isAnnotationPresent(Adaptive.class)) {
        if (cachedAdaptiveClass == null) {
            cachedAdaptiveClass = clazz;
        } else if (!cachedAdaptiveClass.equals(clazz)) {
            throw new IllegalStateException("More than 1 adaptive class found: "
                    + cachedAdaptiveClass.getClass().getName()
                    + ", " + clazz.getClass().getName());
        }
    } else {
    try {
        //类是否为包装类,也就是有以type为参数的构造函数
        clazz.getConstructor(type);
        Set> wrappers = cachedWrapperClasses;
        if (wrappers == null) {
            cachedWrapperClasses = new ConcurrentHashSet>();
            wrappers = cachedWrapperClasses;
        }
        wrappers.add(clazz);
    } catch (NoSuchMethodException e) {
        //没有以type为参数的构造函数
        clazz.getConstructor();
        if (name == null || name.length() == 0) {
            //兼容旧版本的Extension注解
            name = findAnnotationName(clazz);
            if (name == null || name.length() == 0) {
                if (clazz.getSimpleName().length() > type.getSimpleName().length()
                        && clazz.getSimpleName().endsWith(type.getSimpleName())) {
                    name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
                } else {
                    throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
                }
            }
        }
        String[] names = NAME_SEPARATOR.split(name);
        if (names != null && names.length > 0) {
            //类是否有Activate注解,用在getActiveExtension中
            Activate activate = clazz.getAnnotation(Activate.class);
            if (activate != null) {
                cachedActivates.put(names[0], activate);
            }
            for (String n : names) {
                if (!cachedNames.containsKey(clazz)) {
                    cachedNames.put(clazz, n);
                }
                Class c = extensionClasses.get(n);
                if (c == null) {
                    extensionClasses.put(n, clazz);
                } else if (c != clazz) {
                    throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                }
有了对loadExtensionClass的分析,createExtension就比较清楚了
private T createExtension(String name) {
    Class clazz = getExtensionClasses().get(name);
    if (clazz == null) {
        throw findException(name);
    }
    try {
        //加入实例缓存中
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        //对实例进行属性依赖注入
        injectExtension(instance);
        //cachedWrapperClasses在loadFile中会赋值
        Set> wrapperClasses = cachedWrapperClasses;
        //构建包装类,并依赖注入,包装类A,B,B包装type值对应的类,A包装B
        if (wrapperClasses != null && wrapperClasses.size() > 0) {
            for (Class wrapperClass : wrapperClasses) {
                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
            }
        }
        return instance;

接着来看下URl的含义,首先是URL类的代码:
public final class URL implements Serializable {

    private static final long serialVersionUID = -1985165475234910535L;

    private final String protocol;

    private final String username;

    private final String password;

    private final String host;

    private final int port;

    private final String path;

    private final Map, String> parameters;

    //这些属性是volatile,说明在多线程下,
    // 每个属性的值对各个工作内存都是可见的,也就是最新的
    // 也是transient,说明url对象作序列化时,这些属性不会参与进序列化
    // ==== cache ====

    private volatile transient Map, Number> numbers;

    private volatile transient Map, URL> urls;

    private volatile transient String ip;

    private volatile transient String full;

    private volatile transient String identity;

    private volatile transient String parameter;

    private volatile transient String string;
这里举个例子说时url代表的含义:
dubbo://10.33.2.190:20880/com.zyy.service.inter.OrderLogService?anyhost=true&application=order-basic&dubbo=2.5.5&generic=false&interface=com.zyy.service.inter.OrderLogService
&methods=queryOrderLogs,update,insert&pid=9304&side=provider×tamp=1507951011792 &validation=true
上面这个字符串,是一个url.toString的值。
其中的 dubbo 表示协议是用dubbo协议;访问地址是:10.33.2.190;端口号:20880;访问路径是com.zyy.service.inter.OrderLogService;后面的application=order-basic 表示应用名称为order-basic,类似的这些是一些参数值,保存在url的parameters属性中。




你可能感兴趣的:(java,dubbo,dubbo源码分析)