Dubbo—SPI及自适应扩展原理( 六 )

后面会讲到Protocol扩展类都是通过export方法暴露服务 , refer方法引用服务 , 而这两个方法在接口中都标注了@Adaptive注解 , 所以Dubbo为其生成了动态的适配类(这个和Java的动态代理的原理有点像 。 同时我们看到这两个方法中都通过getExtension方法去获取指定的扩展类的实例(这个扩展类名称来自于Invoker(后面会讲)中的url , 因为dubbo是基于url驱动的 , 所有的配置都在url中) 。 这就是Dubbo强大的自适应扩展机制的实现原理 , 我们可以将其运用到我们的项目中去 , 这就是看源码的好处 。 不过还有个问题 , 刚刚在createAdaptiveExtensionClass方法中你一定疑惑compiler是什么 , 它也是调用的getAdaptiveExtension获取适配类 , 这不就进入了死循环么?当然不会 , 首先我们可以去配置文件看看Compiler的扩展类都有哪些:
adaptive=com.alibaba.dubbo.common.compiler.support.AdaptiveCompilerjdk=com.alibaba.dubbo.common.compiler.support.JdkCompilerjavassist=com.alibaba.dubbo.common.compiler.support.JavassistCompiler有一个AdaptiveCompiler类 , 从名字上我们就能猜到它是一个自定义的适配类了 , 然后在其类上可以看到@Adaptive注解验证我们的猜想 , 那么上文也说了在loadFile方法中会将该类赋值给cachedAdaptiveClass变量缓存 , 然后在createAdaptiveExtension -> getAdaptiveExtensionClass方法中获取并实例化对象 , 所以并不会死循环 , 那么在该类中做了什么呢?
public class AdaptiveCompiler implements Compiler { // 这个是在哪赋值的?private static volatile String DEFAULT_COMPILER;public static void setDefaultCompiler(String compiler) {DEFAULT_COMPILER = compiler;}public Class compile(String code, ClassLoader classLoader) {Compiler compiler;ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Compiler.class);String name = DEFAULT_COMPILER; // copy referenceif (name != null} else {// 获取@SPI注解值指定的默认扩展compiler = loader.getDefaultExtension();}return compiler.compile(code, classLoader);}}该适配类会从两个地方获取扩展类 , 先来看看getDefaultExtension:
public T getDefaultExtension() {getExtensionClasses();if(null == cachedDefaultName || cachedDefaultName.length() == 0|| "true".equals(cachedDefaultName)) {return null;}return getExtension(cachedDefaultName);}cachedDefaultName 这个不陌生吧 , 在loadExtensionClasses方法中赋值的 , 其值为@SPI的值 , 这里就是javassist 。 再看另外一条分支 , 是通过DEFAULT_COMPILER的值去获取的 , 这个变量提供了一个setter方法 , 点过去我们可以看到是在ApplicationConfig类中的setCompiler方法调用的 , 因为该类是配置类实例 , 也就是说可以通过dubbo:application的compiler参数来配置编译器类型 , 查看文档 , 也确实有这个配置参数 。 所以看源码能让我们了解平时项目中配置各个参数的意义 , 从而有针对的选择和配置适当的参数 , 而不是一味的照搬文档就完事 。
三、Dubbo IOC在上文中我们看到injectExtension这样一个方法 , 它是做什么的呢?接下来就详细分析它的作用和实现 。
private T injectExtension(T instance) {try {if (objectFactory != null) {for (Method method : instance.getClass().getMethods()) {if (method.getName().startsWith("set")try {String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";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;}


推荐阅读