在dubbo中,爲了靈活的適配一個接口的多種實現,而不是經過硬編碼來指定用哪一個實現,提供了@Adaptive註解。這個註解看的出,在類以及方法上使用的。java
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Adaptive { String[] value() default {}; }
從緩存中獲取,沒有則建立apache
public T getAdaptiveExtension() { Object instance = cachedAdaptiveInstance.get(); // 緩存爲空 if (instance == null) { if (createAdaptiveInstanceError != null) { throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } // 建立 synchronized (cachedAdaptiveInstance) { instance = cachedAdaptiveInstance.get(); if (instance == null) { try { instance = createAdaptiveExtension(); cachedAdaptiveInstance.set(instance); } catch (Throwable t) { createAdaptiveInstanceError = t; throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t); } } } } return (T) instance; }
private T createAdaptiveExtension() { try { // 獲取擴展對象的實例,再注入 return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e); } }
private Class<?> getAdaptiveExtensionClass() { // 獲取SPI信息 getExtensionClasses(); // 緩存有,則返回 if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } // 沒有則建立 return cachedAdaptiveClass = createAdaptiveExtensionClass(); }
private Class<?> createAdaptiveExtensionClass() { // 拼接代碼 String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate(); // 獲取類加載器 ClassLoader classLoader = findClassLoader(); // 獲取編譯器,javassist用於動態編程 org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); // 把代碼編譯成class return compiler.compile(code, classLoader); }
爲了方便後面源碼參考,先貼出拼接好的代碼(我這邊以Protocol爲例)編程
package org.apache.dubbo.rpc; import org.apache.dubbo.common.extension.ExtensionLoader; public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol { public void destroy() { throw new UnsupportedOperationException("The method public abstract " + "void org.apache.dubbo.rpc.Protocol.destroy() of " + "interface org.apache.dubbo.rpc.Protocol is not adaptive method!"); } public int getDefaultPort() { throw new UnsupportedOperationException("The method public abstract int " + "org.apache.dubbo.rpc.Protocol.getDefaultPort() of " + "interface org.apache.dubbo.rpc.Protocol is not adaptive method!"); } public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException { if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null"); if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null"); org.apache.dubbo.common.URL url = arg0.getUrl(); String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException("Failed to get extension " + "(org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])"); org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader. getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName); return extension.export(arg0); } public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException { if (arg1 == null) throw new IllegalArgumentException("url == null"); org.apache.dubbo.common.URL url = arg1; String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException("Failed to get extension " + "(org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])"); org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader. getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName); return extension.refer(arg0, arg1); } }
public String generate() { // no need to generate adaptive class since there's no adaptive method found. // 至少一個方法是有Adaptive註解 if (!hasAdaptiveMethod()) { throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!"); } StringBuilder code = new StringBuilder(); // 拼接package信息 code.append(generatePackageInfo()); // 拼接Imports信息 code.append(generateImports()); // 拼接類名以及實現的接口 code.append(generateClassDeclaration()); // 獲取全部方法,拼接方法 Method[] methods = type.getMethods(); for (Method method : methods) { code.append(generateMethod(method)); } code.append("}"); if (logger.isDebugEnabled()) { logger.debug(code.toString()); } return code.toString(); }
private String generateMethod(Method method) { // 返回值 String methodReturnType = method.getReturnType().getCanonicalName(); // 方法名 String methodName = method.getName(); // 方法內容 String methodContent = generateMethodContent(method); // 方法參數 String methodArgs = generateMethodArguments(method); // 方法異常 String methodThrows = generateMethodThrows(method); return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent); }
其餘比較簡單,咱們只看generateMethodContent緩存
主要是方法體的代碼拼接app
private String generateMethodContent(Method method) { Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class); StringBuilder code = new StringBuilder(512); // 不是Adaptive註解的,方法體內拋異常,詳情見拼接好的代碼 if (adaptiveAnnotation == null) { return generateUnsupported(method); } else { // 獲取url的位置 int urlTypeIndex = getUrlTypeIndex(method); // found parameter in URL type if (urlTypeIndex != -1) { // Null Point check // 拼接判斷參數是否爲空,參考拼接好的refer方法 code.append(generateUrlNullCheck(urlTypeIndex)); } else { // did not find parameter in URL type // 不存在URL,方法體中判斷是否有某個參數的某個方法有URL返回, // 有的話,則返回的字符串參考拼接好的代碼,沒有的話,雷同上面拋異常的拼接字符串,參考拼接好的export方法 // 這個方法,必須是方法名以 get 開頭,或方法名大於3個字符,方法的訪問權限爲 public, // 非靜態方法,方法參數數量爲0,方法返回值類型爲 URL,不符合拋異常 code.append(generateUrlAssignmentIndirectly(method)); } // 若是沒有值,獲取類名的小寫 String[] value = getMethodAdaptiveValue(adaptiveAnnotation); // 是否有Invocation 類型的參數 boolean hasInvocation = hasInvocationArgument(method); // 有的話,則拼接判斷是否爲空的異常代碼 code.append(generateInvocationArgumentNullCheck(method)); // String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); code.append(generateExtNameAssignment(value, hasInvocation)); // check extName == null? // 拼接extName == null這行 code.append(generateExtNameNullCheck(value)); // 拼接getExtension這行 code.append(generateExtensionAssignment()); // return statement // 拼接返回值的最後一行 code.append(generateReturnAndInvocation(method)); } return code.toString(); }