擴展點的Adpative類能夠有兩種方式實現,一種方式是人工實現Adpative類,而後配置成爲該類型的自適應類;另一種方法是若是沒有人工指定的Adpative類,則dubbo的SPI機制會自動生成和編譯一個動態的Adpative類。java
人工設置擴展點自適應實現類會很是靈活,能夠由開發者靈活控制,可是缺點是若是有不少擴展點,自適應邏輯相同或者類似則會出現類爆炸的問題。express
咱們以編譯器擴展點Compiler爲例。apache
擴展點的源碼以下:app
/* * Copyright 1999-2011 Alibaba Group. * * Licensed 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 com.alibaba.dubbo.common.compiler; import com.alibaba.dubbo.common.extension.SPI; /** * Compiler. (SPI, Singleton, ThreadSafe) * * @author william.liangf */ @SPI("javassist") public interface Compiler { /** * Compile java source code. * * @param code Java source code * @param classLoader TODO * @return Compiled class */ Class<?> compile(String code, ClassLoader classLoader); }
它的自適應擴展點實現類AdaptiveCompiler源碼以下:框架
/* * Copyright 1999-2011 Alibaba Group. * * Licensed 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 com.alibaba.dubbo.common.compiler.support; import com.alibaba.dubbo.common.compiler.Compiler; import com.alibaba.dubbo.common.extension.Adaptive; import com.alibaba.dubbo.common.extension.ExtensionLoader; /** * AdaptiveCompiler. (SPI, Singleton, ThreadSafe) * * @author william.liangf */ @Adaptive public class AdaptiveCompiler implements Compiler { private static volatile String DEFAULT_COMPILER; public static void setDefaultCompiler(String compiler) { DEFAULT_COMPILER = compiler; } public Class<?> compile(String code, ClassLoader classLoader) { Compiler compiler; ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class); String name = DEFAULT_COMPILER; // copy reference if (name != null && name.length() > 0) { compiler = loader.getExtension(name); } else { compiler = loader.getDefaultExtension(); } return compiler.compile(code, classLoader); } }
能夠看出自適應實現類自己並無實現compie方法,它是由參數DEFAULT_COMPILER指定一個默認的擴展點名稱,所以是能夠動態調整的。less
因爲大部分的擴展點自適應實現類的代碼邏輯都類似,所以自動生成動態的自適應擴展類則會給開發者帶來很大的便利,省去了不少冗餘代碼。可是因爲dubbo的實現方式是經過代碼來自動生成自適應實現的代碼,代碼可讀性很是差。這也是缺點。咱們經過調試將其生成的代碼打印出來將大大提升代碼可讀性。ui
咱們將以擴展點Protocol爲例來展現生成的代碼。this
/* * Copyright 1999-2011 Alibaba Group. * * Licensed 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 com.alibaba.dubbo.rpc; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.common.extension.Adaptive; import com.alibaba.dubbo.common.extension.SPI; /** * Protocol. (API/SPI, Singleton, ThreadSafe) * * @author william.liangf */ @SPI("dubbo") public interface Protocol { /** * 獲取缺省端口,當用戶沒有配置端口時使用。 * * @return 缺省端口 */ int getDefaultPort(); /** * 暴露遠程服務:<br> * 1. 協議在接收請求時,應記錄請求來源方地址信息:RpcContext.getContext().setRemoteAddress();<br> * 2. export()必須是冪等的,也就是暴露同一個URL的Invoker兩次,和暴露一次沒有區別。<br> * 3. export()傳入的Invoker由框架實現並傳入,協議不須要關心。<br> * * @param <T> 服務的類型 * @param invoker 服務的執行體 * @return exporter 暴露服務的引用,用於取消暴露 * @throws RpcException 當暴露服務出錯時拋出,好比端口已佔用 */ @Adaptive <T> Exporter<T> export(Invoker<T> invoker) throws RpcException; /** * 引用遠程服務:<br> * 1. 當用戶調用refer()所返回的Invoker對象的invoke()方法時,協議需相應執行同URL遠端export()傳入的Invoker對象的invoke()方法。<br> * 2. refer()返回的Invoker由協議實現,協議一般須要在此Invoker中發送遠程請求。<br> * 3. 當url中有設置check=false時,鏈接失敗不能拋出異常,並內部自動恢復。<br> * * @param <T> 服務的類型 * @param type 服務的類型 * @param url 遠程服務的URL地址 * @return invoker 服務的本地代理 * @throws RpcException 當鏈接服務提供方失敗時拋出 */ @Adaptive <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException; /** * 釋放協議:<br> * 1. 取消該協議全部已經暴露和引用的服務。<br> * 2. 釋放協議所佔用的全部資源,好比鏈接和端口。<br> * 3. 協議在釋放後,依然能暴露和引用新的服務。<br> */ void destroy(); }
它生成的擴展自適應實現類源碼以下:url
package com.alibaba.dubbo.rpc; import com.alibaba.dubbo.common.extension.ExtensionLoader; public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol { public void destroy() { throw new UnsupportedOperationException( "method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"); } public int getDefaultPort() { throw new UnsupportedOperationException( "method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"); } public com.alibaba.dubbo.rpc.Exporter export( com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException { if (arg0 == null) throw new IllegalArgumentException( "com.alibaba.dubbo.rpc.Invoker argument == null"); if (arg0.getUrl() == null) throw new IllegalArgumentException( "com.alibaba.dubbo.rpc.Invoker argument getUrl() == null"); com.alibaba.dubbo.common.URL url = arg0.getUrl(); String extName = (url.getProtocol() == null ? "dubbo" : url .getProtocol()); if (extName == null) throw new IllegalStateException( "Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader .getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class) .getExtension(extName); return extension.export(arg0); } public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException { if (arg1 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg1; String extName = (url.getProtocol() == null ? "dubbo" : url .getProtocol()); if (extName == null) throw new IllegalStateException( "Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader .getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class) .getExtension(extName); return extension.refer(arg0, arg1); } }
從生成的源碼能夠看出來這些特色:spa
擴展點源碼以下。
/* * Copyright 1999-2011 Alibaba Group. * * Licensed 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 com.alibaba.dubbo.remoting; import javax.sound.midi.Receiver; import com.alibaba.dubbo.common.Constants; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.common.extension.Adaptive; import com.alibaba.dubbo.common.extension.SPI; /** * Transporter. (SPI, Singleton, ThreadSafe) * * <a href="http://en.wikipedia.org/wiki/Transport_Layer">Transport Layer</a> * <a href="http://en.wikipedia.org/wiki/Client%E2%80%93server_model">Client/Server</a> * * @see com.alibaba.dubbo.remoting.Transporters * @author ding.lid * @author william.liangf */ @SPI("netty") public interface Transporter { /** * Bind a server. * * @see com.alibaba.dubbo.remoting.Transporters#bind(URL, Receiver, ChannelHandler) * @param url server url * @param handler * @return server * @throws RemotingException */ @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY}) Server bind(URL url, ChannelHandler handler) throws RemotingException; /** * Connect to a server. * * @see com.alibaba.dubbo.remoting.Transporters#connect(URL, Receiver, ChannelListener) * @param url server url * @param handler * @return client * @throws RemotingException */ @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY}) Client connect(URL url, ChannelHandler handler) throws RemotingException; }
生成的自適應擴展實現源碼以下。
package com.alibaba.dubbo.remoting; import com.alibaba.dubbo.common.extension.ExtensionLoader; public class Transporter$Adpative implements com.alibaba.dubbo.remoting.Transporter { public com.alibaba.dubbo.remoting.Client connect( com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.remoting.RemotingException { if (arg0 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg0; String extName = url.getParameter("client", url.getParameter( "transporter", "netty")); if (extName == null) throw new IllegalStateException( "Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([client, transporter])"); com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter) ExtensionLoader .getExtensionLoader( com.alibaba.dubbo.remoting.Transporter.class) .getExtension(extName); return extension.connect(arg0, arg1); } public com.alibaba.dubbo.remoting.Server bind( com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.remoting.RemotingException { if (arg0 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg0; String extName = url.getParameter("server", url.getParameter( "transporter", "netty")); if (extName == null) throw new IllegalStateException( "Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([server, transporter])"); com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter) ExtensionLoader .getExtensionLoader( com.alibaba.dubbo.remoting.Transporter.class) .getExtension(extName); return extension.bind(arg0, arg1); } }
區別點是調用了String extName = url.getParameter("server", url.getParameter( "transporter", "netty"));來得到擴展名稱。