在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(); }