您当前的位置: 首页 >  dubbo

Charge8

暂无认证

  • 5浏览

    0关注

    447博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Dubbo SPI源码分析

Charge8 发布时间:2022-07-10 18:14:04 ,浏览量:5

一、ExtensionLoader

ExtensionLoader 表示某个接口的扩展点加载器,可以用来加载某个扩展点实例。

在ExtensionLoader中有几个非常重要的属性:

  1. ConcurrentHashMap EXTENSION_INSTANCES:用来缓存某个接⼝类型所对应的 ExtensionLoader实例
  2. Class type:表示当前 ExtensionLoader实例是哪个接口的扩展点加载器
  3. ExtensionFactory objectFactory:扩展点工厂(对象工厂),可以获得某个对象

ExtensionLoader和 ExtensionFactory的区别在于:

  1. ExtensionLoader最终所得到的对象是Dubbo SPI机制产⽣的
  2. ExtensionFactory最终所得到的对象可能是Dubbo SPI机制所产⽣的,也可能是从Spring容器中所获得的对象

ExtensionLoader中有三个常用的方法:

//表示获取名字为dubbo的扩展点实例 
ExtensionLoader.getExtensionLoader(xxx.class).getExtension("dubbo")
//表示获取⼀个自适应的扩展点实例 
ExtensionLoader.getExtensionLoader(xxx.class).getAdaptiveExtension()
//表示⼀个可以被url激活的扩展点实例
ExtensionLoader.getExtensionLoader(xxx.class).getActivateExtension(URL url, String[] values, String group)
二、Dubbo SPI源码分析

注意:源码中大量使用了缓存,先取缓存,如果没有,再获取,并放到缓存中。

这里使用 Dubbo 中的 Protocol接口扩展点来分析。

    @Test
    public void testProtocol() {
        ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Protocol.class);

        Protocol dubbo = extensionLoader.getExtension("dubbo");
        System.out.println("dubbo 指定名称的扩展点:" + dubbo);
    }

先看一下它的在源码中的配置文件。

@SPI("dubbo")
public interface Protocol {
    int getDefaultPort();

    @Adaptive
     Exporter export(Invoker var1) throws RpcException;

    @Adaptive
     Invoker refer(Class var1, URL var2) throws RpcException;

    void destroy();
}

在这里插入图片描述

1、getExtensionLoader方法

进入 getExtensionLoader方法。 在这里插入图片描述 注意:这个返回的 ExtensionLoader实例对象是 new出来的。

ConcurrentMap> EXTENSION_LOADERS = new ConcurrentHashMap();

每一个 SPI的扩展点接口都会对应一个 ExtensionLoader实例对象。

  • key为接口的全限定名
  • 值为ExtensionLoader实例对象
2、getExtension方法

通过 SPI配置文件中 key=value, 的 key来获取实现类。

  • 在调用 getExtension方法来获取⼀个扩展点实例后,会对实例进行缓存,下次再获取同样名字的扩展点实例时就会从缓存中拿了。 但是,有可能获取到的不是实现类本身,而是实现类的包装类。
2.1 进入 getExtension方法
    public T getExtension(String name) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        // 获取默认扩展类
        if ("true".equals(name)) {
            return getDefaultExtension();
        }

        final Holder holder = getOrCreateHolder(name);
        Object instance = holder.get();

        // 如果有两个线程同时来获取同一个name的扩展点对象,那只会有一个线程会进行创建
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    // 创建扩展点实例对象
                    instance = createExtension(name);  
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

Holder是一个包装,用来存放任何类型值的类。 在这里插入图片描述

2.2 创建扩展点对象

查看 createExtension重点方法。

    private T createExtension(String name) {
        // 获取扩展类  {name: Class}  key-Value  接口的所有实现类
        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, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }

            // 依赖注入 IOC
            injectExtension(instance);

            // AOP
            Set wrapperClasses = cachedWrapperClasses;
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                for (Class wrapperClass : wrapperClasses) {
                    // new XxxWrapper(instance)---包装类实例
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); 
                }
            }

            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }

在调用 createExtension(String name)方法来创建⼀个扩展点实例时,要经过以下几个步骤:

  1. 根据name找到对应的扩展点实现类
  2. 根据实现类生成⼀个实例,把实现类和对应生成的实例进行缓存
  3. 对生成出来的实例进行依赖注入(给实例的属性进行赋值)
  4. 对依赖注入后的实例进行AOP(Wrapper),把当前接口类的所有的Wrapper全部⼀层⼀层包裹在实例对象上,每包裹个Wrapper后,也会对Wrapper对象进行依赖注入。
  5. 返回最终的Wrapper对象
2.2.1 加载SPI配置文件

加载SPI配置文件,获取扩展类 {name: Class} key-Value 接口的所有实现类。

(1)查看getExtensionClasses 方法

getExtensionClasses()是⽤来加载当前接⼝所有的扩展点实现类并进⾏缓存,次需要加载时直接拿缓存中的返回⼀个Map。然后可以从这个 Map中按照指定的name获取对应的扩展点实现类。 在这里插入图片描述

(2)查看 loadResource方法

查看 loadDirectory方法 => 循环 loadResource方法。 loadResource方法就是完成对⽂件内容的解析,按⾏进⾏解析,会解析出"="两边的内容。

  • "="左边的内容,就是扩展点的name,
  • 右边的内容就是扩展点实现类,并且会利用ExtensionLoader类的类加载器来加载扩展点实现类。

然后调用 loadClass方法对name和扩展点实例进行详细的解析,并且最终把他们放到 Map中去。 在这里插入图片描述

(3)查看 loadClass方法

查看 loadDirectory方法 => 循环 loadResource方法 =>loadClass方法。

在这里插入图片描述

针对配置文件中的写法,大概有三种处理方式。

  1. 当前扩展点实现类上是否存在@Adaptive注解,如果存在则把该类认为是当前接⼝的默认⾃适应类(接口代理类),并把该类存到 cachedAdaptiveClass属性上。
  2. 当前扩展点实现是否是⼀个当前接口的⼀个Wrapper类
  3. 当前扩展点实现类是否有无参数的构造方法,如果有多个name,则判断⼀下当前扩展点实现类上是否存在@Activate注解,如果存在,则把该类添加到cachedActivates中,cachedWrapperClasses是⼀个map。最后,遍历多个name,把每个name和对应的实现类存到extensionClasses中去,extensionClasses 就是上⽂所提到的map。

至此,加载类就走完了。

2.2.2 Dubbo中的IOC

查看 injectExtension(instance)方法。

在这里插入图片描述

2.2.3 Dubbo中的AOP

遍历 包装类,包装套娃。

            // AOP
            Set wrapperClasses = cachedWrapperClasses;
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                for (Class wrapperClass : wrapperClasses) {
                    // new XxxWrapper(instance)---包装类实例
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); 
                }
            }
3、getAdaptiveExtension()方法

自适应扩展点对象,也就是某个接口的代理对象是通过Dubbo内部生成代理类,然后生成代理对象的。

在Dubbo中,这种机制就是可以通过@Adaptive注解来指定某个类为某个接口的代理类,如果指定了,Dubbo在生成自适应扩展点对象时实际上生成的就是 @Adaptive注解所注解的类的实例对象。 如果未指定,由Dubbo默认实现的,生成代理类的。

3.1 创建接口实例的核心方法

进入 getAdaptiveExtension()方法 =》 createAdaptiveExtension核心方法。

在这里插入图片描述

createAdaptiveExtensionClass方法就是Dubbo中默认生成 Adaptive类实例的逻辑。这个实例就是当前这个接口的⼀个代理对象。

3.1.1 先看 getAdaptiveExtensionClass方法

在这里插入图片描述 (1)进入getExtensionClasses方法

这个方法主要做的是,加载配置文件(通过流的方式),建立映射关系,loadExtensionClasses方法前面看过了。

在这里插入图片描述

(2)进入 createAdaptiveExtensionClass方法

进入 createAdaptiveExtensionClass方法。动态编译生成字符串类(字节重组),生成 .class文件加载到 JVM中并返回。

在这里插入图片描述 AdaptiveClassCodeGenerator生成接好的字符串类,最后,通过 compile加载到 JVM内存,并返回Class对象。

3.1.2 再看 injectExtension方法

这个 查看 injectExtension方法。Dubbo中的IOC,前面看过了。

4、getActivateExtension方法

Activate扩展点: 每个扩展点都有⼀个name,通过这个name可以获得该name对应的扩展点实例,但是有的场景下,希望⼀次性获得多个扩展点实例。比如示例:

	@Test
	public void test2() {
		ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Filter.class);
		URL url = new URL("http://", "localhost", 8080);
		url = url.addParameter("cache", "test");
		List activateExtensions = extensionLoader.getActivateExtension(url, new String[] { "validation" }, CommonConstants.CONSUMER);
		for (Filter activateExtension : activateExtensions) {
			System.out.println(activateExtension);
		}
	}

在这里插入图片描述

4.1 查看 getActivateExtension方法
    public List getActivateExtension(URL url, String[] values, String group) {
        List exts = new ArrayList();
        List names = values == null ? new ArrayList(0) : Arrays.asList(values);

        // 想要获取的filter的名字不包括"-default"
        if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
            getExtensionClasses();
            // 缓存了被Activate注解标记了的类(已经被加载进来了的前提下),表示这个扩展点在什么时候能用
            // 遍历所有的Activate的扩展类
            for (Map.Entry entry : cachedActivates.entrySet()) {
                String name = entry.getKey();
                Object activate = entry.getValue();

                String[] activateGroup, activateValue;

                if (activate instanceof Activate) {
                    activateGroup = ((Activate) activate).group();
                    activateValue = ((Activate) activate).value();
                } else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
                    activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
                    activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
                } else {
                    continue;
                }

                // group表示想要获取的Filter所在的分组,activateGroup表示当前遍历的Filter所在的分组,看是否匹配
                // names表示想要获取的Filter的名字,name表示当前遍历的Filter的名字
                // 如果当前遍历的Filter的名字不在想要获取的Filter的names内,并且names中也没有要排除它,则根据url看能否激活
                if (isMatchGroup(group, activateGroup)
                        && !names.contains(name)
                        && !names.contains(REMOVE_VALUE_PREFIX + name)
                        // 查看url的参数中是否存在key为activateValue,并且对应的value不为空
                        && isActive(activateValue, url)) {
                    exts.add(getExtension(name));
                }
            }
            exts.sort(ActivateComparator.COMPARATOR);
        }

        // 直接根据names来获取扩展点
        List usrs = new ArrayList();
        for (int i = 0; i             
关注
打赏
1664721914
查看更多评论
0.2880s