dubbo SPI @Adaptive源码解读
接上文dubbo SPI @Adaptive注解使用方法与原理解析
目录
- 概览
getAdaptiveExtension
——入口createAdaptiveExtension
- 1
getAdaptiveExtensionClass
- 1.1
getExtensionClasses
- 1.1.1
loadExtensionClasses
- 1.1.1
- 1.2
createAdaptiveExtensionClass
- 1.1
- 2
injectExtension
- 1
概览
自适应类的用法示例
SimpleExt simpleExt= ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension();
接口代码示例
@SPI("impl1")
public interface SimpleExt {
String echo(URL url,String s);
@Adaptive({ "key4"})
void printA(URL url);
@Adaptive
void printB(URL url);
@Adaptive({ "key3","key2","key1"})
void printC(URL url);
}
getAdaptiveExtension
方法的大致流程如下图(从左到右)
接下来对每个方法进行解读。
getAdaptiveExtension
——入口
此方法是获取自适应类的入口
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;
}
createAdaptiveExtension
当前面的方法没有获取带自适应类实例的缓存时,会进入此方法。这个方法里一共做了三件事。
getAdaptiveExtensionClass
先获取自适应类newInstance
再生成对应的实例injectExtension
然后为实例注入扩展类属性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);
}
}
1getAdaptiveExtensionClass
这里将分为两步骤
getExtensionClasses
加载扩展点文件。若实现类上有@Adaptive
注解,会被缓存起来,只能存在一个自适应类的缓存,默认是不可覆盖的,并且后续的执行方法都是用的此实现类。createAdaptiveExtensionClass
创建自适应类(仅在自适应类缓存为空时进入此方法)private Class<?> getAdaptiveExtensionClass() {
//加载配置文件
getExtensionClasses();
//若自适应类有缓存则返回
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//自适应类的缓存为空则创建
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
1.1getExtensionClasses
此方法是dubbo SPI的核心方法,获取扩展类都需要经过这步(包括getDefalultExtension
,getAdaptiveExtension
,getActivateExtension
)。同样还是先希望从缓存取,不存在就去加载文件填充缓存。
private Map<String, Class<?>> getExtensionClasses() {
//取缓存
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
//缓存无,则从文件加载扩展点
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
1.1.1loadExtensionClasses
这里也分为两步骤
cacheDefaultExtensionName
获取扩展点名称并缓存。例如@SPI("impl1")
则是将”impl1”缓存下来loadDirectory
解析扩展点文件并缓存实现类。例如配置文件内容为impl1=com.example.demo.impl.SimpleExtImpl1
,则会以key=“impl1” value= SimpleExtImpl1类的键值对 存在Map<String, Class<?>>
中。除了进行普通扩展类的缓存,此方法还会额外做三种缓存(自适应类,包装扩展类,自动激活类)以加快后续方法的执行效率。private Map
> loadExtensionClasses() { //获取扩展点名称并缓存
cacheDefaultExtensionName();
Map<String, Class<?>> extensionClasses = new HashMap<>();
for (LoadingStrategy strategy : strategies) {
//加载指定文件夹下的配置文件
loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
//同时加载alibaba和apache的类
loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
}
return extensionClasses;
}
1.2 createAdaptiveExtensionClass
此方法将生成一个动态的自适应类,它能根据参数决定执行方法的实现类,主要做了三件事。
生成类代码字符串。生成代码的逻辑主要分为7步。
1.1 生成与扩展点相同位置的package。import
ExtensionLoader
这个类。为了不写其他的import方法,其他调用都使用全路径名。同时设置类名为“接口名称+$Adaptive”的格式。例如SimpleExt
接口会生成SimpleExt&Adaptive
。1.2 遍历接口的所有方法,获取方法的返回类型,参数类型,异常类型等。
1.3 生成校验参数是否为空的代码。
1.4 生成默认实现类名称。如果
@Adaptive
注解没有默认值,则会先根据此名称去寻找它的扩展实现类。如SimpleExt
的默认类名称为simple.ext。1.5 生成获取扩展点名称的代码。例如对于前面的示例接口SimpleExt的printC方法,会生成
url.getParameter("key3", url.getParameter("key2",url.getParameter("key1","impl1")));
1.6 生成获取具体实现类的代码。通过上一步获得的extName执行getExtension(extName) 方法获取实现类。
1.7 生成用此实现类调用方法的代码。
- 获取编译器的自适应类。实现
Compile
接口的AdaptiveCompiler
类上有@Adaptive
注解,因此此类会作为编译器的默认实现。 使用编译器编译类
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);
}
2injectExtension
这一部分是为了给新创建的扩展点实现类注入属性。只有当此属性也为扩展点(且该扩展点已被加载并生成实例)时才能注入。
主要逻辑是遍历该类的所有方法,查看setter方法的返回类型对应的类是否为扩展点,是的话将其通过反射调用此setter方法注入。
private T injectExtension(T instance) {
//此工厂中存储了已经实例化的扩展点,若工厂不为空,则查找工厂中是否拥有可注入的类
if (objectFactory == null) {
return instance;
}
try {
//通过反射获取并遍历实现类的所有方法
for (Method method : instance.getClass().getMethods()) {
//只遍历以"set"命名开头的方法
if (!isSetter(method)) {
continue;
}
/** * Check {@link DisableInject} to see if we need auto injection for this property */
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
//获得该构造方法第一个参数类型
Class<?> pt = method.getParameterTypes()[0];
//若是基础类型则跳过,如 int,double,String等
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
//获取该属性名,如"setVersion" => "version"
String property = getSetterProperty(method);
//从扩展类工厂中获取该属性的实例
Object object = objectFactory.getExtension(pt, property);
//如果成功获取,则将该扩展类实例注入进去
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("Failed to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
整体的流程就到此结束了,代码多处是反射和缓存的应用,如有错误欢迎指出~
如果有疑问,欢迎评论~
如果成功解决了你的问题,点个赞再走吖~
还没有评论,来说两句吧...