Dubbo的自适应扩展原理
Dubbo支持多种协议,多种序列化方式,多注册中心等等,使用起来非常的灵活简便。内置了很多常见的组件,比如协议支持dubbo, redis,thrift,grpc,hessian等,也支持用户自定义协议去完成业务功能。这种灵活便捷,正是Dubbo的SPI带来的好处。有时,有些扩展我们不想在框架启动时就直接加载,而是希望在扩展方法被调用的时候,根据运行时的参数去加载具体的扩展类。这么一说,是不是很快就想到了一种实现方式:动态代理。我们可以直接根据每一个SPI接口,封装出一个代理类,由代理类去找到对应的扩展实现,然后调用扩展方法。我们来举一个例子说明这个问题:
interface Reporter {
String report ( URL url) ;
}
class ChineseReporter implements Reporter {
@Override
String report ( URL url) {
return "你好" ;
}
}
class EnglishReporter implements Reporter {
@Override
String report ( URL url) {
return "hello" ;
}
}
现在我们如果想根据传入的url参数中属性nation,来决定初始化对应实例,调用该方法,应该怎么做呢,是不是要新编写一个代理类,来帮助我们完成这个功能
public class AdaptiveReporter implements Reporter {
@Override
public String report ( URL url) {
if ( url == null) {
throw new IllegalArgumentException ( "url == null" ) ;
}
String nation = url. getParameter ( "nation" ) ;
if ( nation == null) {
throw new IllegalArgumentException ( "nation == null" ) ;
}
Reporter reporter = ExtensionLoader
. getExtensionLoader ( Reporter. class ) . getExtension ( nation) ;
return reporter. report ( url) ;
}
}
Dubbo的自适应扩展正是这么实现的,只不过这里我们不需要主动为每一个SPI接口编写代理类,而是由Dubbo自动为我们生成具有代理功能的代码,通过javassist或者jdk编译这个代理类,得到Class对象,通过反射创建出代理实例。
源码分析(apache dubbo 2.7.8)
自使用扩展实现中有一个很重要的注解@Adaptive,先来看一下它的说明
@Documented
@Retention ( RetentionPolicy. RUNTIME)
@Target ( { ElementType. TYPE, ElementType. METHOD} )
public @interface Adaptive {
String[ ] value ( ) default { } ;
Adaptive注解可以放在类上,也可以放在方法上,放在类上不会为该类生成代理类,一般情况下,Adaptive注解都是放在SPI接口的方法上面。通过ExtensionLoader的getAdaptiveExtension方法,获取到自适应扩展实例,我们就来分析下该方法。
@SuppressWarnings ( "unchecked" )
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;
}
@SuppressWarnings ( "unchecked" )
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) ;
}
}
首先会先检查缓存,没有则去创建,然后设置到缓存中。创建时,包含了三层逻辑:
调用 getAdaptiveExtensionClass 方法获取自适应拓展 Class 对象 通过反射进行实例化 调用 injectExtension 方法向拓展实例中注入依赖
为什么这里第3步,还要注入依赖,我们生成的代理类,默认是不包含外部依赖的。原来Dubbo存在两种自适应扩展,一种是自动生成的,一种是手动编写的,手工编写的自适应扩展很可能会存在一些依赖,所以这里是为了手动编写的扩展类做注入的。我们接着分析getAdaptiveExtensionClass方法
private Class< ? > getAdaptiveExtensionClass ( ) {
getExtensionClasses ( ) ;
if ( cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass ( ) ;
}
private Class< ? > createAdaptiveExtensionClass ( ) {
String code = new AdaptiveClassCodeGenerator ( type, cachedDefaultName) . generate ( ) ;
ClassLoader classLoader = findClassLoader ( ) ;
org. apache. dubbo. common. compiler. Compiler compiler = ExtensionLoader. getExtensionLoader ( org. apache. dubbo. common. compiler. Compiler. class ) . getAdaptiveExtension ( ) ;
return compiler. compile ( code, classLoader) ;
}
先检查实现类中有没有标识Adaptive注解的,存在直接返回该实现类,否则就创建出代理类,并编译成Class对象,这里重点在于generate方法,是如何生成具有代理功能的code
public String generate ( ) {
if ( ! hasAdaptiveMethod ( ) ) {
throw new IllegalStateException ( "No adaptive method exist on extension " + type. getName ( ) + ", refuse to create the adaptive class!" ) ;
}
StringBuilder code = new StringBuilder ( ) ;
code. append ( generatePackageInfo ( ) ) ;
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) ;
}
private String generateMethodContent ( Method method) {
Adaptive adaptiveAnnotation = method. getAnnotation ( Adaptive. class ) ;
StringBuilder code = new StringBuilder ( 512 ) ;
if ( adaptiveAnnotation == null) {
return generateUnsupported ( method) ;
} else {
int urlTypeIndex = getUrlTypeIndex ( method) ;
if ( urlTypeIndex != - 1 ) {
code. append ( generateUrlNullCheck ( urlTypeIndex) ) ;
} else {
code. append ( generateUrlAssignmentIndirectly ( method) ) ;
}
String[ ] value = getMethodAdaptiveValue ( adaptiveAnnotation) ;
boolean hasInvocation = hasInvocationArgument ( method) ;
code. append ( generateInvocationArgumentNullCheck ( method) ) ;
code. append ( generateExtNameAssignment ( value, hasInvocation) ) ;
code. append ( generateExtNameNullCheck ( value) ) ;
code. append ( generateExtensionAssignment ( ) ) ;
code. append ( generateReturnAndInvocation ( method) ) ;
}
return code. toString ( ) ;
}
以上就是生成代理类code的大体过程了,中间有一些关于如何查找到url中参数中的value,即查询到对应的扩展类name,没有进行细致的分析,读者可以参考Adaptive注解中的说明进行分析。下面来看一下具体生成的一个自适应扩展类code(经过格式化以后的)
package org. apache. dubbo. common. extension. adaptive;
import org. apache. dubbo. common. URL;
import org. apache. dubbo. common. extension. Adaptive;
import org. apache. dubbo. common. extension. SPI;
@SPI
public interface HasAdaptiveExt {
@Adaptive
String echo ( URL url, String s) ;
}
package org. apache. dubbo. common. extension. adaptive;
import org. apache. dubbo. common. extension. ExtensionLoader;
public class HasAdaptiveExt $Adaptive implements org. apache. dubbo. common. extension. adaptive. HasAdaptiveExt {
public java. lang. String echo ( org. apache. dubbo. common. URL arg0, java. lang. String arg1) {
if ( arg0 == null)
throw new IllegalArgumentException ( "url == null" ) ;
org. apache. dubbo. common. URL url = arg0;
String extName = url. getParameter ( "has.adaptive.ext" , "adaptive" ) ;
if ( extName == null)
throw new IllegalStateException ( "Failed to get extension (org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt) name from url (" + url. toString ( ) + ") use keys([has.adaptive.ext])" ) ;
org. apache. dubbo. common. extension. adaptive. HasAdaptiveExt extension = ( org. apache. dubbo. common. extension. adaptive. HasAdaptiveExt) ExtensionLoader. getExtensionLoader ( org. apache. dubbo. common. extension. adaptive. HasAdaptiveExt. class ) . getExtension ( extName) ;
return extension. echo ( arg0, arg1) ;
}
}