1-1 获取扩展类实现
上一篇文章已经介绍过了,Dubbo通过ExtensionLoader.getExtension(String name)根据实现类名称获取具体的扩展类实现,其中真正创建扩展类实现的方法是createExtension(String name)。
源码如下:
private T createExtension(String name) {
// 获取所有扩展类Class对象
// 根据name获取Class对象
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
// 获取缓存中的对象
// 如果缓存中没有,那么通过反射创建对象
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 依赖注入
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
// 使用Wrapper类包装
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
该方法主要进行了如下6步处理:
- 获取所有的扩展类Class对象
- 根据name获取具体的扩展类Class对象
- 通过扩展类Class对象获取缓冲中已实例化过的扩展类对象
- 如果没获取到,则通过反射实例化扩展类对象
- 对扩展类对象进行依赖注入
- 通过包装类包装扩展类对象(装饰器模式)。
这里笔者主要对1、5这两个步骤进行分析,分析完这两个步骤,读者应该就了解Dubbo的SPI是如何解决实例化只能简单通过反射创建,如果要对创建好的对象进行二次包装,只能用户手动完成。这个问题的了。
在分析完这两个步骤之后,为了使读者阅读起来没有那么突兀,笔者会再次分析createExtension(String name)这个方法的整体处理逻辑。
1.1 获取所有扩展类的Class对象
Dubbo的SPI配置文件也可以放在META-INF/services目录下,除此之外,还可以放在其他的地方,例如: META-INF/dubbo/,SPI模块就是对这两个位置的配置文件进行解析,然后获取所有的扩展类的Class对象的。
具体的处理方法就是getExtensionClasses()方法,源码如下:
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;
}
Dubbo为扩展类的解析也创建了一个缓存cachedClasses用于存储解析结果(一个key为String,value为Class类型的数组),毕竟这是文件读取操作,而且配置文件是不会动态改变的。真正解析配置的方法是loadExtensionClasses()方法:
private Map<String, Class<?>> loadExtensionClasses() {
cacheDefaultExtensionName();
Map<String, Class<?>> extensionClasses = new HashMap<>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
return extensionClasses;
}
该方法主要完成了下面两步操作:
- 通过
cacheDefaultExtensionName()方法获取当前接口的默认扩展名。 - 通过类加载器加载所有配置文件中的扩展类。
Dubbo的SPI模块使用接口上的@SPI注解指定默认实现扩展名称,例如Dubbo协议名默认为dubbo,org.apache.dubbo.rpc.Protocol接口源码如下:
@SPI("dubbo")
public interface Protocol {}
cacheDefaultExtensionName()方法正是读取这个注解,获取的默认扩展名称:
private void cacheDefaultExtensionName() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if (names.length > 1) {
throw new IllegalStateException("More than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if (names.length == 1) {
cachedDefaultName = names[0];
}
}
}
}
cacheDefaultExtensionName()还处理了@SPI注解中的名称被,分隔的情况,默认采用第一个名称。
在获取了接口的默认扩展后,通过loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type)方法,Dubbo加载了所有扩展类的Class对象,配置文件所在目录为:
- DUBBO_INTERNAL_DIRECTORY: META-INF/dubbo/internal/
- DUBBO_DIRECTORY:META-INF/dubbo/
- SERVICES_DIRECTORY:META-INF/services/
在调用loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type)方法时有的进行了类名更换操作,例如:
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
这是因为Dubbo在未开源到Apache,默认使用的包名是com.alibaba。加载扩展实现类分3个步骤:
- 获得类加载器,加载目录:loadDirectory
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) { // 文件名 String fileName = dir + type; try { Enumeration<java.net.URL> urls; // 获取类加载器 ClassLoader classLoader = findClassLoader(); // 类加载器加载配置文件 if (classLoader != null) { urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); } if (urls != null) { while (urls.hasMoreElements()) { java.net.URL resourceURL = urls.nextElement(); // 加载配置文件 loadResource(extensionClasses, classLoader, resourceURL); } } } catch (Throwable t) { logger.error("Exception occurred when loading extension class (interface: " + type + ", description file: " + fileName + ").", t); } } - 解析配置文件:loadResource
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) { try { try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) { String line; // 分行读取配置文件 while ((line = reader.readLine()) != null) { // 忽略注释 final int ci = line.indexOf('#'); if (ci >= 0) { line = line.substring(0, ci); } line = line.trim(); if (line.length() > 0) { try { // =号分隔 String name = null; int i = line.indexOf('='); if (i > 0) { name = line.substring(0, i).trim(); line = line.substring(i + 1).trim(); } if (line.length() > 0) { // 根据类名加载实现类 loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name); } } catch (Throwable t) { IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t); exceptions.put(line, e); } } } } } catch (Throwable t) { logger.error("Exception occurred when loading extension class (interface: " + type + ", class file: " + resourceURL + ") in " + resourceURL, t); } } - 加载扩展类Class文件:loadClass
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { // 如果实现类不是接口类的子类 报错 if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error occurred when loading extension class (interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + " is not subtype of interface."); } if (clazz.isAnnotationPresent(Adaptive.class)) { // 指定了自适应扩展,不需要自动生成代码 // 修改cachedAdaptiveClass cacheAdaptiveClass(clazz); } else if (isWrapperClass(clazz)) { // 是装饰器类 // 修改cachedWrapperClasses cacheWrapperClass(clazz); } else { // 单纯的扩展类 // 获取无参构造器,没有就会抛出异常 clazz.getConstructor(); if (StringUtils.isEmpty(name)) { name = findAnnotationName(clazz); if (name.length() == 0) { throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); } } String[] names = NAME_SEPARATOR.split(name); // 尝试修改cachedActivates维护激活点 // 尝试维护缓冲实例名 // 尝试维护扩展类名 if (ArrayUtils.isNotEmpty(names)) { cacheActivateClass(clazz, names[0]); for (String n : names) { cacheName(clazz, n); saveInExtensionClass(extensionClasses, clazz, name); } } } }通过loadClass方法,将扩展实现分为4类:
- 自适应扩展类:不需要自动生成代码,典型代表
ExtensionFactory - 装饰器类:构造器是要求接口类型的类
- 带激活点的类:具有无参构造器,被Adaptive修饰的类
- 通常类:具有无参构造器的类
- 自适应扩展类:不需要自动生成代码,典型代表
Dubbo通过包装类实现了对扩展实现进行类似于AOP的包装,但Dubbo要求包装类必须使用装饰器模式。
1.2 依赖注入
通过getExtensionClasses()方法,Dubbo将扩展实现分为4类:
- 自适应扩展类:不需要自动生成代码,典型代表
ExtensionFactory - 装饰器类
- 带激活点的类
- 通常实现类
由装饰器类帮助对剩下三种进行包装,但是某些扩展实现需要其他的Bean作为依赖,因此,Dubbo提供了自动注入的功能,该功能由injectExtension(T instance)方法实现:
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (isSetter(method)) {
// 被DisableInject修饰,则不进行自动注入
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
// 如果方法的第一个参数是基本类型,则跳过
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
// 判断是否是setter方法
// 获取参数名
String property = getSetterProperty(method);
// 根据参数名寻找对应扩展实现类
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
// 使用setter方法注入
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;
}
通过injectExtension方法中的依赖注入可以看到,进行依赖注入时,bean都是通过objectFactory即ExtensionFactory类获取的。
1.3 回顾createExtension
前面两个主要的方法分析完毕了,我们再次回到createExtension方法:
private T createExtension(String name) {
// 获取所有扩展类Class对象
// 根据name获取Class对象
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
// 获取缓存中的对象
// 如果缓存中没有,那么通过反射创建对象
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 依赖注入
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
// 使用Wrapper类包装
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
// 依赖注入包装器
// 使用装饰器封装类型实现
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}