dubbo 系列之Spi(一)

Dubbo SPI

Dubbo中最核心的扩展逻辑就是Spi机制了,我们今天来看

Jdk的Spi的实现

将一下为啥要用这个功能吧,主要就是扩展性强,将不同的实现进行封装,调用方去决定用哪个
我们看一下Jdk的实现,
首先需要个接口,

public interface Log {

    void println();
}

添加两个的实现类

public class Log4j implements Log {
    @Override
    public void println() {
        System.out.println("log4j println ");
    }
}
public class Logback implements Log {
    @Override
    public void println() {
        System.out.println("Logback println ");
    }
}
结构如图所示,注意一下这个META-INF/services这个文件夹得创建,建议可以用个文件占位

在这里插入图片描述
这个META-INF/services下面就放对应的实现的接口的文件描述

com.peng.demo.spi.logImpl.Log4j
com.peng.demo.spi.logImpl.Logback

然后我看一下这个怎么获取到对应的实现

  ServiceLoader<Log> serviceLoader =

                ServiceLoader.load(Log.class);

        Iterator<Log> iterator = serviceLoader.iterator();

        while (iterator.hasNext()) {

            Log log = iterator.next();

            System.out.println("JDK SPI");

            log.println();

        }

我们看一下源码的实现
java.util.ServiceLoader#reload核心代码

 // Cached providers, in instantiation order
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
     public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }

然后这个就返回了lookupIterator,我们看一下这个定义

 private class LazyIterator
        implements Iterator<S>
    {

        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        Iterator<String> pending = null;
        String nextName = null;

        private LazyIterator(Class<S> service, ClassLoader loader) {
            this.service = service;
            this.loader = loader;
        }

        private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                    //这个就是对应的文件路径
                    String fullName = PREFIX + service.getName();
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x);
                }
            }
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                //这个就是文件的读取
                pending = parse(service, configs.nextElement());
            }
            nextName = pending.next();
            return true;
        }

        private S nextService() {
            if (!hasNextService())
                throw new NoSuchElementException();
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                //加载 nextName字段指定的类 
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
                fail(service,
                     "Provider " + cn + " not found");
            }
            if (!service.isAssignableFrom(c)) {
                fail(service,
                     "Provider " + cn  + " not a subtype");
            }
            try {
                //初始化实例
                S p = service.cast(c.newInstance());
                //放到对应的缓存中
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated",
                     x);
            }
            throw new Error();          // This cannot happen
        }

        public boolean hasNext() {
            if (acc == null) {
                return hasNextService();
            } else {
                PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                    public Boolean run() { return hasNextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

        public S next() {
            if (acc == null) {
                return nextService();
            } else {
                PrivilegedAction<S> action = new PrivilegedAction<S>() {
                    public S run() { return nextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
        public Iterator<S> iterator() {
             return new Iterator<S>() {

            Iterator<Map.Entry<String,S>> knownProviders
                = providers.entrySet().iterator();

            public boolean hasNext() {
                if (knownProviders.hasNext())
                    return true;
                return lookupIterator.hasNext();
            }

            public S next() {
                if (knownProviders.hasNext())
                    return knownProviders.next().getValue();
                return lookupIterator.next();
            }

            public void remove() {
                throw new UnsupportedOperationException();
            }

        };
    }

    }

上面是Jdk,原生的实现类,想一下它这个的弊端。
一个就是初始化加载的时候,需要将对应的实例都初始化,没办法按需加载
同时文件中只是存放对应的实例的名称,没办法做的区分,这个还是和上面逻辑类似。

Dubbo Spi的实现

首先对文件层面做了约定
分为META-INF/dubbo/services,META-INF/dubbo/internal,META-INF/services
META-INF/dubbo/services 存放用户自定义的Spi
META-INF/dubbo/internal 存放dubbo内部的Spi
META-INF/services 存放兼容jdk的Spi
同时存放的文件,也做了对应的修改,改成了kv的结构
随便找了一个看一下

curator=org.apache.dubbo.remoting.zookeeper.curator.CuratorZookeeperTransporter

curator5=org.apache.dubbo.remoting.zookeeper.curator5.Curator5ZookeeperTransporter

这样我们就可以根据名字对对不同的实现进行实例化了
下面找个dubbo的spi看一下
这个是负载均衡的Spi接口

// 这个就是该接口是扩展接口,这个名称就是默认的实现
@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {

    /**
     * select one invoker in list.
     *
     * @param invokers   invokers.
     * @param url        refer url
     * @param invocation invocation.
     * @return selected invoker.
     */
    @Adaptive("loadbalance")
    <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;

org.apache.dubbo.common.extension.ExtensionLoader
看一下这个具体的Spi的加载类

public class ExtensionLoader<T> {
	/**
	 * :Dubbo 中一个扩展接口对应一个 ExtensionLoader 实例,该集合缓存了全部 ExtensionLoader 实例,其中的 Key 为扩展接口,Value 为加载其扩展实现的 ExtensionLoader 实例。
	 */
    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
	/**
	* 该集合缓存了扩展实现类与其实例对象的映射关系。
	*/
    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
	/**
	* 当前 ExtensionLoader 实例负责加载扩展接口
	*/
    private final Class<?> type;

    private final ExtensionFactory objectFactory;
    /**
    * 扩展实现类和名称的关系
    */
   private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
	/**
	* 名称和扩展实现类的关系
	*/
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();

    private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();
    /**
    *缓存了该 ExtensionLoader 加载的扩展名与扩展实现对象之间的映射关系。
    */
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
    private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
    private volatile Class<?> cachedAdaptiveClass = null;
    private String cachedDefaultName;
    private volatile Throwable createAdaptiveInstanceError;

    private Set<Class<?>> cachedWrapperClasses;

    private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();
    /**
     * Record all unacceptable exceptions when using SPI
     */
    private Set<String> unacceptableExceptions = new ConcurrentHashSet<>();
	/**
	* 这个就是上面说的对应加载的策略,下面是对应的三个实现类
	* DubboInternalLoadingStrategy  
	* DubboLoadingStrategy
	* ServicesLoadingStrategy
	*/
    private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
    
 }   

org.apache.dubbo.common.extension.ExtensionLoader#getExtension(java.lang.String, boolean)

public T getExtension(String name, boolean wrap) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        final Holder<Object> holder = getOrCreateHolder(name);
        Object instance = holder.get();
        if (instance == null) {//双重检测
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    //初始化扩展类
                    instance = createExtension(name, wrap);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

org.apache.dubbo.common.extension.ExtensionLoader#createExtension

private T createExtension(String name, boolean wrap) {
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null || unacceptableExceptions.contains(name)) {
            throw findException(name);
        }
        try {
            //缓存中获取值
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                //反射初始化
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            //属性注入逻辑,下面细分析
            injectExtension(instance);


            if (wrap) {

                List<Class<?>> wrapperClassesList = new ArrayList<>();
                if (cachedWrapperClasses != null) {
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                    Collections.reverse(wrapperClassesList);
                }

                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                    for (Class<?> wrapperClass : wrapperClassesList) {
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        if (wrapper == null
                                || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                                //这个就是aop的逻辑
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                        }
                    }
                }
            }

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

现在我们了解了Spi的作用,动态加载对应的扩展类
我们看一下这个@Adaptive作用

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.dubbo.common.extension;

import org.apache.dubbo.common.URL;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Provide helpful information for {@link ExtensionLoader} to inject dependency extension instance.
 *
 * @see ExtensionLoader
 * @see URL
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
    /**
     * Decide which target extension to be injected. The name of the target extension is decided by the parameter passed
     * in the URL, and the parameter names are given by this method.
     * <p>
     * If the specified parameters are not found from {@link URL}, then the default extension will be used for
     * dependency injection (specified in its interface's {@link SPI}).
     * <p>
     * For example, given <code>String[] {"key1", "key2"}</code>:
     * <ol>
     * <li>find parameter 'key1' in URL, use its value as the extension's name</li>
     * <li>try 'key2' for extension's name if 'key1' is not found (or its value is empty) in URL</li>
     * <li>use default extension if 'key2' doesn't exist either</li>
     * <li>otherwise, throw {@link IllegalStateException}</li>
     * </ol>
     * If the parameter names are empty, then a default parameter name is generated from interface's
     * class name with the rule: divide classname from capital char into several parts, and separate the parts with
     * dot '.', for example, for {@code org.apache.dubbo.xxx.YyyInvokerWrapper}, the generated name is
     * <code>String[] {"yyy.invoker.wrapper"}</code>.
     *
     * @return parameter names in URL
     */
    String[] value() default {};

}

@Adaptive注解用来实现 Dubbo 的适配器功能,这个适配器的逻辑下片再写,还有上面的注入和wrapper。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值