SPI是一種擴展機制,在java中SPI機制被普遍應用,好比Spring中的SpringServletContainerInitializer 使得容器啓動的時候SpringServletContainerInitializer 執行onStartup方法。在dubbo中,dubbo實現了本身的spi擴展機制,下面詳細的講解下,dubbo的擴展機制。java
dubbo中的fiter是使用spi機制實現的典型應用,如今使用filter做爲例子,展現先dubbo的spi機制。git
首先定義一個filter。github
public class MyFilter implements Filter { public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { System.out.println("開始調用 MyFilter"); Result result = invoker.invoke(invocation); System.out.println("結束調用 MyFilter"); return result; } }
將filter加到dubbo默認的filter服務器
@Activate(group = {Constants.CONSUMER})
在消費者的resource文件下建app
/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filteride
內容爲:源碼分析
myFilter=com.lenny.sample.dubbo.common.filter.MyFilter
在消費端指定filterui
@Reference(version = "1.0.0",filter = "myFilter") private UserDubboService userDubboService;
在調用userDubboService的服務器時在控制檯打印以下內容:url
在服務端的resource文件下建.net
/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter
內容爲:
myFilter=com.lenny.sample.dubbo.common.filter.MyFilter
在服務提供者指定filter:
@Service(version = "1.0.0",filter = "myFilter")
一樣在控制檯打印以下內容:
dubbo默認提供一些filter。
Filter的加載源碼位於ProtocolFilterWrapper類,具體實現過程
// 服務端暴露 @Override public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) { return protocol.export(invoker); } return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER)); } // 消費端 @Override public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException { if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) { return protocol.refer(type, url); } return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER); }
主要看buildInvokerChain。
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) { Invoker<T> last = invoker; // 獲取所有的filter List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group); if (!filters.isEmpty()) { for (int i = filters.size() - 1; i >= 0; i--) { final Filter filter = filters.get(i); final Invoker<T> next = last; last = new Invoker<T>() { @Override public Class<T> getInterface() { return invoker.getInterface(); } @Override public URL getUrl() { return invoker.getUrl(); } @Override public boolean isAvailable() { return invoker.isAvailable(); } @Override public Result invoke(Invocation invocation) throws RpcException { // 執行filter的invoke方法 return filter.invoke(next, invocation); } @Override public void destroy() { invoker.destroy(); } @Override public String toString() { return invoker.toString(); } }; } } return last; }
在執行export的時候獲取所有的filter,綁定到要執行的方法上面,在要執行的方面的時候,執行filter方法。
在ExtensionLoader中有loadExtensionClasses方法,加載所有的擴展類。
private static final String SERVICES_DIRECTORY = "META-INF/services/"; private static final String DUBBO_DIRECTORY = "META-INF/dubbo/"; private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/"; private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*"); private Map<String, Class<?>> loadExtensionClasses() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation != null) { String value = defaultAnnotation.value(); if ((value = value.trim()).length() > 0) { String[] names = NAME_SEPARATOR.split(value); if (names.length > 1) { throw new IllegalStateException("more than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names)); } if (names.length == 1) cachedDefaultName = names[0]; } } Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); // 去相應的文件路徑下掃描文件 loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY); loadDirectory(extensionClasses, DUBBO_DIRECTORY); loadDirectory(extensionClasses, SERVICES_DIRECTORY); return extensionClasses; }
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) { // 文件路徑加上全類名 String fileName = dir + type.getName(); try { Enumeration<java.net.URL> urls; ClassLoader classLoader = findClassLoader(); if (classLoader != null) { urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); } if (urls != null) { while (urls.hasMoreElements()) { java.net.URL resourceURL = urls.nextElement(); // 加載資源文件 loadResource(extensionClasses, classLoader, resourceURL); } } } catch (Throwable t) { logger.error("Exception when load extension class(interface: " + type + ", description file: " + fileName + ").", t); } }
根據使用@SPI的註解的類全類名去以下路徑,找到相應問文件,解析文件內容
如以filter爲例:
package com.alibaba.dubbo.rpc; import com.alibaba.dubbo.common.extension.SPI; @SPI public interface Filter { Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException; }
Filter的全類名是com.alibaba.dubbo.rpc.Filter。因此能夠去掃描以下路徑
META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter
META-INF/services/com.alibaba.dubbo.rpc.Filter
在上面的例子中將MyFilter的文件定義到了META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter路徑下。
固然也能夠放到META-INF/dubbo/com.alibaba.dubbo.rpc.Filter和META-INF/services/com.alibaba.dubbo.rpc.Filter。
如圖:放到下面三個位置任何一個位置都是能夠的。
本文源代碼:https://github.com/applenele/sample