1. 程式人生 > 其它 >Dubbo釋出過程中,擴充套件點的載入

Dubbo釋出過程中,擴充套件點的載入

在Dubbo服務釋出的過程中,第一次出現擴充套件點的載入是在doExportUrlsFor1Protocol()方法中,載入ConfiguratorFactory配置工廠。

        if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .hasExtension(url.getProtocol())) {
            url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
        }

首先根據載入類型別獲取擴充套件類載入器,然後根據擴充套件點名稱獲取具體的擴充套件點。先從快取中獲取對應型別的擴充套件器,如果快取中不存在,則利用建構函式建立一個新的載入器,並放入到快取中。

public class ExtensionLoader<T> {

    private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }
    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!");
        }
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type +
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }

        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;
    }


    public boolean hasExtension(String name) {
        if (name == null || name.length() == 0)
            throw new IllegalArgumentException("Extension name == null");
        try {
            this.getExtensionClass(name);
            return true;
        } catch (Throwable t) {
            return false;
        }
    }

    private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

    public T getAdaptiveExtension() {
        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;
    }

    private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }
}

呼叫過程為:

getExtensionClass(String name)
  ->getExtensionClass(String name)

建立DubboProtocol的過程為,其中包含多個Wrapper的包裝:

Dubbo是URL驅動的。

.getAdaptiveExtension():獲取的是自適應類,是根據@Adaptive擴充套件點,動態生成class code,並經過編譯生成的動態代理類。
另一個常用的方法是:getExtension:用來獲取寬擴充套件類例項。

Protocol是Protocol$Adaptive,實際呼叫的是DubboProtocol
ProxyFactoty是 ProxyFactoty$Adaptive,實際呼叫的是JavassistProxyFactory,用來生成代理類和呼叫者。
此處程式碼意思是:使用DubboProtocol協議在本地暴露一個使用JavassistProxyFactory生成的代理類的呼叫者。

    private void exportLocal(URL url) {
        if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
            URL local = URL.valueOf(url.toFullString())
                    .setProtocol(Constants.LOCAL_PROTOCOL)
                    .setHost(LOCALHOST)
                    .setPort(0);
            ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref));
            Exporter<?> exporter = protocol.export(
                    proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
            exporters.add(exporter);
            logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
        }
    }

injectExtension()方法,用來處理方法名帶有set()的方法,動態的設定處理引數。例如:此處的協議為動態,根據URL中配置的引數而定,所以添加了一個注入的方法。injectExtension()方法中,判斷到有set()開始的方法,通過Object object = objectFactory.getExtension(pt, property)得到一個動態代理類Type$Adaptive,然後將該動態代理物件設定到引數中

public class StubProxyFactoryWrapper implements ProxyFactory {
    public void setProtocol(Protocol protocol) {
        this.protocol = protocol;
    }
}
public class ExtensionLoader<T> {
    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())) {
                        Class<?> pt = method.getParameterTypes()[0];
                        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;
    }
}

另一個常用的方法是:getActivateExtension(URL url, String[] values, String group):獲取啟用點

@SPI
public interface ExtensionFactory {

}
@SPI註解,用來提供獲取ExtensionFactory的實現類:public class SpiExtensionFactory
其中的isWrapperClass判斷規則是:實現類的建構函式是否是包含載入類引數。如果是,則判斷為包裝類,並將包裝類新增到cachedWrapperClasses成員變數中,載入類中不新增。
@Adaptive註解:當某個類上標註@Adaptive註解時,在loadClass的過程中,由於判斷類上有@Adaptive註解,會將載入的類新增到cachedAdaptiveInstance成員變數中,作為在載入擴充套件點類時的返回物件。

注意 JavassistProxyFactory類中getInvoker()方法,final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);

##
@Activate註解在Filter中有使用案例:

@Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.CACHE_KEY)
public class CacheFilter implements Filter {
}


在呼叫export()方法的過程當中,首先對Filter進行了載入。
階段性總結export()過程:
在export()過程中,首先呼叫了包裝DubboProtocol的Wrapper類,包括三個類,由外到內依次是:QosProtocolWrapper、ProtocolListenerWrapper,ProtocolFilter,由外層逐級呼叫內層,再呼叫到ProtocolFilterWrapper類的時候,呼叫了一個構建呼叫鏈的方法, protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER)),其中Constants.SERVICE_FILTER_KEY=service.filter;Constants.PROVIDER = provider,其中,在URL的引數中,service.filter的value為null,所以在載入了filter的所有實現類之後,根據Activate的條件對Filter進行過濾;其中此處的過濾條件為 :group為group;在此過程中符合條件的Filtle有8個:
```java
0 = {ExceptionFilter@11068} 
1 = {ClassLoaderFilter@11268} 
2 = {EchoFilter@11442} 
3 = {MonitorFilter@11832} 
4 = {GenericFilter@11974} 
5 = {TimeoutFilter@11997} 
6 = {TraceFilter@12018} 
7 = {ContextFilter@12032} 

然後對Filter進行迴圈處理,迴圈建立invoker,並呼叫上一個invoker的invoker方法。
然後呼叫InjvmProtocol的export()方法進行服務暴露,構造了一個InjvmExporter物件

    InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
        super(invoker);
        this.key = key;//key=com.bail.user.service.IUserService:1.0.0
        this.exporterMap = exporterMap;
        exporterMap.put(key, this);//將InjvmExporter自己放入到了map容器中
    }

在exportLocal方法返回的Exporter為一個ListenerExporterWrapper型別的,並將該物件放入到exporters容器中,exporters為ServiceBean的一個成員變數。
ServiceConfig處的export,protocol值為registry.

接下來看DubboProtocol的釋出過程:
構建DubboExporter
openServer