這里繼續dubbo的源碼旅程,在過程中學習它的設計和技巧,看優秀的代碼,我想對我們日程編碼必然有幫助的。而那些開源的代碼正是千錘百煉的東西,希望和各位共勉。
拿ProtocolListenerWrapper為例子,看源碼的時候發現它是一個裝飾類的標准實現有一個自身的復制構造函數,把被包裝者復制進來,然后結合裝飾部分的操作。看下ProtocolListenerWrapper類有這樣的代碼:
public class ProtocolListenerWrapper implements Protocol { private final Protocol protocol; public ProtocolListenerWrapper(Protocol protocol){ if (protocol == null) { throw new IllegalArgumentException("protocol == null"); } this.protocol = protocol; } public int getDefaultPort() { return protocol.getDefaultPort(); } public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) { return protocol.export(invoker); } return new ListenerExporterWrapper<T>(protocol.export(invoker), Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class) .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY))); } 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 new ListenerInvokerWrapper<T>(protocol.refer(type, url), Collections.unmodifiableList( ExtensionLoader.getExtensionLoader(InvokerListener.class) .getActivateExtension(url, Constants.INVOKER_LISTENER_KEY))); } public void destroy() { protocol.destroy(); } }
try { clazz.getConstructor(type); Set<Class<?>> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } catch (NoSuchMethodException e) {
上面也可以看到用異常作為一個判斷邏輯。
ExtensionLoader中getExtension(String name)方法中會調用createExtension(String name)這個方法中將cachedWrapperClasses利用了起來,具體實現就是將被裝飾類實例作為參數調用warpper類的自身復制構造函數,這樣就會把被裝飾累包裝起來,從而達到,當有調用被裝飾類的方法是就可以執行到warpper中的邏輯代碼了,實現都是調用了clazz.getConstructor方法,代碼片段:
Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (wrapperClasses != null && wrapperClasses.size() > 0) { for (Class<?> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } }
public abstract class ExporterListenerAdapter implements ExporterListener { public void exported(Exporter<?> exporter) throws RpcException { } public void unexported(Exporter<?> exporter) throws RpcException { } }
這是個技巧吧,剛剛上面提到自己要寫擴展類的時候就不直接繼承ExporterListener了,因為直接繼承接口會強制要求實現兩個方法的,而實際編碼中dubbo的作者應該也發現這兩個方法是完全不同的業務時使用,所有我們可以只繼承ExporterListenerAdapter,如此自己的業務代碼中就不需要出現一個空方法了。
實際擴展dubbo時,我們這這樣寫:
@Activate public class ExportListenerTest extends ExporterListenerAdapter{ public void exported(Exporter<?> exporter) throws RpcException { Class<?> exportClz = exporter.getInvoker().getInterface(); System.out.println(exportClz.getName()); } }
然后用插件機制,在resource/META-INF/dubbo下新建一個文件:
文件名:com.alibaba.dubbo.rpc.ExporterListener
內容:
exportListenerTest=com.test.dubbo.service.listener.ExportListenerTest
如此我們在發布一個方法時都會調用到ExportListenerTest的exported方法。