`
jishuaige
  • 浏览: 10438 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

dubbo-探索之旅(二)---扩展JDK的SPI

阅读更多
  在此感谢http://blog.csdn.net/quhongwei_zhanqiu/article/details/41577235。斩秋的文章。以下的大部分是在参考了斩秋的文章。
 

   上一节简单的对dubbo的外围知识进行了简单的打探,下面开始真正进入dubbo。开启dubbo的源码探索之旅。
  JDK自带的SPI正因为有在上一节提到的缺点,因此在dubbo中对其进行了扩展。这节重点来看看dubbo是怎么扩展JDK的SPI。

dubbo中定义了注解:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {
   /**
     * 缺省扩展点名。
     */
   String value() default "";
}

在dubbo中只有在接口打了@SPI注解的接口,它的实现类才会被当成扩展点的实现进行查找。
例如:接口:Protocol 被SPI注解标注,默认扩展点的名字为dubbo
 @SPI("dubbo")
 public interface Protocol {
  //省
 }


此接口的实现类(相关类):



dubbo中RPC模块默认协议(Protocol )的实现是:DubboProtocol,key:dubbo

  这里插一句:dubbo加载扩展点的地方:
  1)META-INF/dubbo/internal/   //dubbo内部实现的各种扩展都放在了这个目录了
  2)META-INF/dubbo/
  3)META-INF/services/

因此定义DubboProtocol的文件在:

查看com.alibaba.dubbo.rpc.protocol文件中的内容:
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol。Protocol的其中一个扩展点就在此定义。


在文件中定义了接口的扩展点之后,最重要的就是读取配置文件中的信息,并且按需加载相应的扩展点的实现。在dubbo中ExtensionLoader类负责加载扩展点的实现。
下面来重点看看ExtensionLoader类
  首先对ExtensionLoader说明一下:
  1)每一个ExtensionLoader实例仅负责加载被SPI注解扩展的实现
  2)每一个ExtensionLoader实例只负责加载一个特定扩展点实现
  3)每一个扩展点(一个被SPI注解的接口)对应最多只有一个ExtensionLoader实例
  4)每一个扩展点对应最多只有一个ExtensionLoader实例
  5)对于每一个扩展点的每一个实现类,只会有一个实例。


下面介绍一下ExtensionLoader类中的几个重点方法:

1,ExtensionLoader.getExtensionLoader(Protocol.class)
   根据接口类型创建一个ExtensionLoader并且放入缓存中:EXTENSION_LOADERS(一个静态对象),如果已经存在了该接口类型的ExtensionLoader实例,就直接返回。
2,loadExtensionClasses():
   读取扩展点中的实现类
   1)先读取SPI注解的value值,如果有作为默认扩展实现类的key。
   2)依次读取路径的文件
      META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol
      META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol
      META-INF/services/com.alibaba.dubbo.rpc.Protocol
3,loadFile():
   逐行读取com.alibaba.dubbo.rpc.Protocol文件中的内容,每行内容以key/value形式存储。
   1)判断实现类上有没有被@Adaptive注解:
      11)如果被@Adaptive的注解,将此实现类作为Protocol的适配类缓存起来,赋值给cachedAdaptiveClass属性,(只能有一个@Adaptive实现类,出现第二个就会报错了)。
      12)如果没有被@Adaptive注解,
        121)判断实现类是否存在入参为接口的构造器,如果存在作为包装类缓存到Set<Class<?>> cachedWrapperClasses中(装饰模式)
        122)如果不是适配类不是包装类,就是扩展点的具体实现对象。将实现类缓存到cachedClasses属性中。

4,创建或获取适配对象getAdaptiveExtension
   1)如果cachedAdaptiveClass有值,说明有且仅有一个实现类打了@Adaptive。
       Object instance = cachedAdaptiveInstance.get();
   2)如果cachedAdaptiveClass为空, 创建适配类字节码
      为什么要创建适配类?一个接口多种实现,SPI机制也是如此,这是策略模式。但是我们在代码执行过程中选择哪种具体的策略呢?Dubbo采用统一数据模式com.alibaba.dubbo.common.URL(它是dubbo定义的数据模型不是jdk的类),它会穿插于系统的整个执行过程,URL中定义的协议类型字段protocol,会根据具体业务设置不同的协议。url.getProtocol()值可以是dubbo也是可以webservice。
     适配类的作用是根据url.getProtocol()的值extName,去ExtensionLoader. getExtension( extName)选取具体的扩展点实现。

插一句:
    能够利用javasist生成适配类的条件
      1)接口方法中必须至少有一个方法打上了@Adaptive注解
      2)打上了@Adaptive注解的方法参数必须有URL类型参数或者有参数中存在getURL()方法。

5,通过createAdaptiveExtensionClassCode()会生成Protocol接口的适配类的java源码代码。如果生成的适配类源码要被java虚拟机加载执行的话,那必须编译成字节码。dubbo提供两种方式去执行代码的编译1)利用JDK工具类编译2)利用javassit根据源代码生成字节码。
后面会专门探索一下dubbo中的compiler接口。

@Adaptive注解在实现类上和在接口方法上的区别:
1)在实现类上:通过获取扩展文件的时候遇到注解@Adaptive,就把这个类作为适配类缓存在ExtensionLoader中,调用的时候直接返回。
2)在接口上:首先生成JAVA代码。在通过编译器编译成class加载。

6,自动Wrap上扩展点的Wrap类
     我们还是拿protocol来说,ProtocolFilterWrapper, ProtocolListenerWrapper这个两个类是装饰对象实现protocol,用来增强其他扩展点实现的功能。ProtocolFilterWrapper功能主要是在refer 引用远程服务的中透明的设置一系列的过滤器链用来记录日志,处理超时,权限控制等等功能;ProtocolListenerWrapper在provider的exporter,unporter服务和consumer的refer服务,destory调用时添加监听器,dubbo提供了扩展但是没有默认实现哪些监听器。

     Dubbo是如何自动的给扩展点wrap上装饰对象的呢?
       1)在ExtensionLoader.loadFile加载扩展点配置文件的时候
          对扩展点类有接口类型为参数的构造器就是包转对象,缓存到集合中去
       2)在调ExtensionLoader的createExtension(name)根据扩展点key创建扩展的时候, 先实例化扩展点的实现, 在判断时候有此扩展时候有包装类缓存,有的话利用包转器增强这个扩展点实现的功能。
     
       private T createExtension(String name) {
        Class<?> clazz = getExtensionClasses().get(name);//获取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);//扩展点注入
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && wrapperClasses.size() > 0) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    //循环遍历所有wrapper实现,实例化wrapper并进行扩展点注入
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }
      


7. dubbo的ExtensionLoader在加载扩展实现的时候内部实现了个简单的ioc机制来实现对扩展实现所依赖的参数的注入,dubbo对扩展实现中公有的set方法且入参个数为一个的方法,尝试从对象工厂ObjectFactory获取值注入到扩展点实现中去。
     /**
     * 扩展点参数的自动注入
     * @param instance
     * @return
     */
    private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {
                for (Method method : instance.getClass().getMethods()) {
                    if (method.getName().startsWith("set")
                            && method.getParameterTypes().length == 1
                            && Modifier.isPublic(method.getModifiers())) {// 处理所有set方法
                        Class<?> pt = method.getParameterTypes()[0];// 获取set方法参数类型
                        try {
                            // 获取setter对应的property名称
                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                            Object object = objectFactory.getExtension(pt, property);// 根据类型,名称信息从ExtensionFactory获取
                            if (object != null) {
                                method.invoke(instance, object);
                                // 如果不为空,说set方法的参数是扩展点类型
                                // instance对象调用method方法,参数为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;
    }


   AdaptiveExtensionFactory持有所有ExtensionFactory对象的集合,dubbo内部默认实现的对象工厂是SpiExtensionFactory和SpringExtensionFactory,他们存放在ArrayList对象中,getExtension(Class<T> type, String name)遍历获取Extension对象,如果遍历完集合中没有找到扩展对象,就返回NULL。
   1)SpiExtensionFactory工厂获取要被注入的对象,就是要获取dubbo spi扩展的实现,所以传入的参数类型必须是接口类型并且接口上打上了@SPI注解,返回的是一个适配类对象。
   2) SpringExtensionFactory,Dubbo利用spring的扩展机制跟spring做了很好的融合。在发布或者去引用一个服务的时候,会把spring的容器添加到SpringExtensionFactory工厂集合中去, 当SpiExtensionFactory没有获取到对象的时候会遍历SpringExtensionFactory中的spring容器来获取要注入的对象。

最后附上一个活动图(参照斩秋博主的所画):





总结:
ExtensionLoader加载扩展点的各种类型的实现类
   适配类:每个扩展点只会有一个适配实现类出现。如果没有找到适配类,会使用JDK动态代理或者是javasist动态生成一个。
   装饰类:实现类是否存在入参为接口的构造函数,如果有就是装饰类。可以多个
   实现类:如果不是适配类不是包装类,就是扩展点的具体实现对象

以上是我个人的对dubbo的一些见解,欢迎大家一起讨论!

下一节,我想看看ExtensionFactory
  • 大小: 37 KB
  • 大小: 13.3 KB
  • 大小: 149.7 KB
分享到:
评论

相关推荐

    dubbo-admin-2.5.4及dubbo-monitor-2.5.3 安装及配置

    dubbo-admin安装要点: 1.清空tomcat/webapps/ROOT目录 2.将包解压到tomcat/webapps/ROOT下 3.修改tomcat/webapps/ROOT/WEB-INF/dubbo.properties 文件 dubbo.registry.address dubbo接口服务注册地址: 单机...

    dubbo-Admin JDK8编译

    【Dubbo-Admin JDK8 编译详解】 Dubbo-Admin 是阿里巴巴开源的分布式服务治理平台,它提供了服务注册、服务发现、服务监控等能力,是Dubbo框架的重要组成部分。在JDK8环境下编译Dubbo-Admin,可以确保与Java 8的...

    dubbo-admin在jdk1.8环境下运行

    dubbo-admin在jdk1.8环境下运行,dubbo-admin在jdk1.8环境下运行dubbo-admin在jdk1.8环境下运行dubbo-admin在jdk1.8环境下运行dubbo-admin在jdk1.8环境下运行dubbo-admin在jdk1.8环境下运行dubbo-admin在jdk1.8环境下...

    dubbo-admin jdk1.8可用

    【标题】"dubbo-admin jdk1.8可用"指的是Dubbo管理控制台dubbo-admin在Java Development Kit(JDK)版本1.8环境下可以正常运行。Dubbo是中国阿里巴巴开源的一个高性能、轻量级的服务治理框架,它提供服务发现、服务...

    dubbo-admin jdk8

    【标题】"dubbo-admin jdk8" 涉及的核心知识点主要集中在 Dubbo 的管理控制台 Dubbo-admin 和其对 Java 开发环境 JDK8 的依赖。Dubbo 是一款高性能、轻量级的开源 Java RPC 框架,主要用于服务治理,而 Dubbo-admin ...

    dubbo-admin-2.5.4-SNAPSHOT-jdk8

    【标题】"dubbo-admin-2.5.4-SNAPSHOT-jdk8"指的是Dubbo管理控制台的一个特定版本,适用于Java 8环境。Dubbo是阿里巴巴开源的一款高性能、轻量级的服务治理框架,主要用于实现分布式服务的治理,提供服务的注册、...

    dubbo-dubbo-2.7.3.rar

    dubbo源码dubbo-dubbo-2.7.3.rardubbo源码dubbo-dubbo-2.7.3.rardubbo源码dubbo-dubbo-2.7.3.rardubbo源码dubbo-dubbo-2.7.3.rardubbo源码dubbo-dubbo-2.7.3.rardubbo源码dubbo-dubbo-2.7.3.rardubbo源码dubbo-dubbo...

    dubbo-admin-jdk1.8环境可用.zip

    【标题】"dubbo-admin-jdk1.8环境可用.zip" 涉及的主要知识点是Dubbo Admin的兼容性问题,特别是与Java 1.8版本的集成。Dubbo是一款高性能、轻量级的开源Java RPC框架,由阿里巴巴提供。在分布式系统中,它主要负责...

    dubbo-admin的下载

    根据描述,本教程推荐的dubbo-admin版本是与JDK 1.8及以上版本兼容的,这意味着你需要先确保你的开发环境中已经安装了Java 8或者更高版本。 1. **Java环境配置**:首先,你需要在你的机器上安装Java Development ...

    解决dubbo-admin在jdk1.8下启动报错

    解决dubbo-admin在jdk1.8下启动报错问题,需要下载dubbo源码修改问题重新编译打包发布,这里已经和重新编译。

    dubbo-admin-2.5.4-jdk1.8

    dubbo -admin-2.5.4 不兼容 jdk1.8版本 会报错 下面修改后的dubbo-admin -jdk1.8 亲测可用 下载解压后 改成 工程ROOT 全部放在Tomcat webapps 下 作为ROOT 目录即可使用~~~

    incubator-dubbo-dubbo-2.5.8

    【标题】"incubator-dubbo-dubbo-2.5.8" 是一个基于Java的开源服务框架,由Apache孵化器项目提供。这个版本是Dubbo的2.5.8稳定版,它包含了Dubbo的核心服务治理功能以及一个专门的管理平台——dubbo-admin。 【描述...

    dubbo-admin-2.5.4.war

    【标题】"dubbo-admin-2.5.4.war" 指的是 Dubbo 提供的一款基于 Web 的管理控制台应用,版本为 2.5.4。这个 WAR 文件是一种打包好的 Java Web 应用程序,可以在支持 Java Servlet 的容器(如 Apache Tomcat)上部署...

    dubbo-demo-consumer、dubbo-demo-provider、dubbo-simple-monitor

    本篇将详细讲解基于dubbo-demo-consumer、dubbo-demo-provider和dubbo-simple-monitor的实例服务,带你深入理解Dubbo的核心概念和操作流程。 首先,我们来看`dubbo-demo-consumer`,它是Dubbo服务的消费者。消费者...

    incubator-dubbo-dubbo-2.6.1

    【标题】"incubator-dubbo-dubbo-2.6.1" 是一个Apache Incubator项目Dubbo的特定版本,这里的2.6.1表示该版本是Dubbo的稳定分支之一。 【描述】提到的"incubator-dubbo-dubbo-2.6.1"表明这是Apache孵化器中的Dubbo...

    dubbo-admin-jdk8

    JDK8引入了模块系统(Project Jigsaw),尽管`dubbo-admin`本身并不直接依赖这个特性,但作为一个独立的服务治理工具,它可以借鉴模块化的思想进行设计,提高代码的可维护性和可扩展性。 4. 兼容性与稳定性 JDK8的...

    incubator-dubbo-ops-master.rar

    《Apache Incubator Dubbo-OPS Master:深度解析与实践》 Apache Incubator Dubbo-OPS Master 是一套由Dubbo社区开发的管理工具,旨在为Dubbo服务提供全面的运营管理和监控支持。Dubbo,作为一款高性能、轻量级的...

    dubbo-admin包

    【标题】"dubbo-admin包"是Dubbo框架的一个重要组成部分,主要用作服务治理的管理界面。这个压缩包包含了运行Dubbo管理控制台所需的所有文件,使得开发者和运维人员可以方便地监控、管理和配置Dubbo服务。 【描述】...

    dobbo源码dubbo-dubbo-2.7.3.rar

    dobbo源码dubbo-dubbo-2.7.3.rardobbo源码dubbo-dubbo-2.7.3.rardobbo源码dubbo-dubbo-2.7.3.rardobbo源码dubbo-dubbo-2.7.3.rardobbo源码dubbo-dubbo-2.7.3.rardobbo源码dubbo-dubbo-2.7.3.rardobbo源码dubbo-dubbo...

    dubbo-admin-2.5.4.war后台管理

    其中,`dubbo-admin`是Dubbo的核心组件之一,它提供了一个可视化的管理控制台,帮助开发者对服务进行管理和监控。本文将详细解析`dubbo-admin-2.5.4.war`这个版本的后台管理工具,探讨其主要特性和使用方法。 首先...

Global site tag (gtag.js) - Google Analytics