上一片着重分析了ExtensionLoader在实际应用中的几个核心方法的工作原理,这篇将作为上一篇的补充,重点分析ExtensionLoader源码中使用的几个核心技术点。 在ExtensionLoader的源码分析中曾提到过一个objectFactory属性未做详细解释,这里将给你解密。
不积跬步,无以至千里;不积小流,无以成江河。
- ExtensionFactory原理
顾名思义,ExtensionFactory是一个扩展点的工厂类。
/**
* ExtensionFactory
* 扩展点工厂,加载接口的实现类。这个接口包括Dubbo中的SPI接口和一般类型的接口
*/
@SPI
public interface ExtensionFactory {
/**
* Get extension.
* 获取扩展点实例
* @param type object type 接口类型.
* @param name object name 接口的扩展点名称.
* @return object instance 扩展点实例.
*/
<T> T getExtension(Class<T> type, String name);
}
从源码可以看出:
① 这是一个SPI接口
② 接口中只有一个方法getExtension 用于获取接口的扩展点实例。
③ 接口中有两个参数,一个是接口的类型,一个扩展实例的名称
我们在看一下ExtensionFactory接口在Dubbo框架中结构层次:
从上图可以看出 ExtensionFactory接口有三个实现类:
① SpiExtensionFactory
获取dubbo容器中SPI接口扩展点实例工厂,具体实现我们来撸源码:
/**
* SpiExtensionFactory
* Dubbo SPI扩展点工厂类,主要功能是从Dubbo容器中获取SPI接口的默认的扩展点
*/
public class SpiExtensionFactory implements ExtensionFactory {
@Override
public <T> T getExtension(Class<T> type, String name) {
//1要求type必须是一个接口,并且有@SPI注解。这是dubbo中SPI接口的标准配置
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
//2调用ExtensionLoader的静态方法获取type接口的ExtensionLoader实例,getExtensionLoader逻辑很简单,想了解的自己翻阅源码
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
//3 loader.getSupportedExtensions()返回的是扩展点名称的TreeSet集合
if (!loader.getSupportedExtensions().isEmpty()) {
//4使用ExtensionLoader#getAdaptiveExtension()获取默认的实现类,这个方法在上一篇中有详细介绍
return loader.getAdaptiveExtension();
}
}
//不满足1就直接返回null
return null;
}
}
从SpiExtensionFactory 的实现可以看出,底层还是使用的ExtensionLoader#getAdaptiveExtension() 。而参数name在整个过程没有被使用。这里获取SPI接口实例的关键是接口type
② SpringExtensionFactory
这是Dubbo整合Spring框架时,获取spring的bean容器中的实例的工厂类
/**
* SpringExtensionFactory
* dubbo与spring容器的整合
* 通过SpringExtensionFactory可以获取到spring容器中的扩展类
*/
public class SpringExtensionFactory implements ExtensionFactory {
private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class);
/**
* 自动去重的set集合保存spring的上下文对象
*/
private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>();
/**
* Dubbo自动关闭的监听器,当监听到spring应用关闭事件时,自动关闭dubbo
*/
private static final ApplicationListener SHUTDOWN_HOOK_LISTENER = new ShutdownHookListener();
public static void addApplicationContext(ApplicationContext context) {
CONTEXTS.add(context);
if (context instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) context).registerShutdownHook();
DubboShutdownHook.getDubboShutdownHook().unregister();
}
ApplicationContextUtils.addApplicationListener(context, SHUTDOWN_HOOK_LISTENER);
}
public static void removeApplicationContext(ApplicationContext context) {
CONTEXTS.remove(context);
}
public static Set<ApplicationContext> getContexts() {
return CONTEXTS;
}
// currently for test purpose
public static void clearContexts() {
CONTEXTS.clear();
}
/**
* 从spring容器中获取指定class类型和名称的对象
* @param type object type. 扩展点类型
* @param name object name. 扩展点名称
* @param <T> 扩展点class
* @return 扩展点
*/
@Override
@SuppressWarnings("unchecked")
public <T> T getExtension(Class<T> type, String name) {
/*
* SPI should be get from SpiExtensionFactory
* 如果扩展类时一个接口,并且接口上由@SPI注解,就换回null。
* 意思是:SPI接口的扩展点实现应该从SpiExtensionFactory中获取
*/
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
return null;
}
//遍历Spring的上下文对象ApplicationContext
for (ApplicationContext context : CONTEXTS) {
//通过接口类型和接口实现的名称从上下文中获取接口的实例对象,如果有多个实现,默认获取第一个
T bean = BeanFactoryUtils.getOptionalBean(context, name, type);
if (bean != null) {
return bean;
}
}
logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " + type.getName());
//Spring容器中没有找到就返回null
return null;
}
private static class ShutdownHookListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextClosedEvent) {
DubboShutdownHook shutdownHook = DubboShutdownHook.getDubboShutdownHook();
shutdownHook.doDestroy();
}
}
}
}
由实现逻辑可以看出,就是在spring容器的上下文对象作为静态变量存入SpringExtensionFactory中,获取接口实例时就遍历spring的上下文,从中根据接口类型和实现类的名称去查找,有就返回,没有就返回null
③ AdaptiveExtensionFactory
标注了@Adaptive注解,可见这个是ExtensionFactory默认的实现类。AdaptiveExtensionFactory有个List factories参数
/**
* AdaptiveExtensionFactory
* 由于本实现类上有@Adaptive注解,因此它才是ExtensionFactory的默认实现
* 其本身包含ExtensionFactory的所有实现类
* 在获取接口实例时,就遍历其他的ExtensionFactory实例。调用他们的getExtension方法
*/
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
/**
* 存放SpiExtensionFactory ,SpringExtensionFactory实例
*/
private final List<ExtensionFactory> factories;
/**
* 在构造方法中就加载所有的ExtensionFactory的实例
*/
public AdaptiveExtensionFactory() {
//ExtensionLoader#getExtensionLoader((Class<T> type)获取ExtensionFactory对应的ExtensionLoader实例
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
/*
ExtensionLoader.getSupportedExtensions()返回的TreeSet集合,
里面会对ExtensionFactory进行排序,默认排序会使SpiExtensionFactory实例排在前面
这样就会优先从Dubbo的SPI容器中获取扩展点,如果获取不到再从SpringExtensionFactory容器中获取 。
*/
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
//并使用不可变的list存到内存中
factories = Collections.unmodifiableList(list);
}
@Override
public <T> T getExtension(Class<T> type, String name) {
/*
获取扩展点实例,实际是调用SpiExtensionFactory,SpringExtensionFactory等的getExtension 。 妙啊!!!
*/
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
}
到这里就能明白了ExtensionFactory接口的工作流程,具体功能实现在SpiExtensionFactory和SpringExtensionFactory中,而AdaptiveExtensionFactory作为默认实现,主要作用是用于管理协调其他的实现。
下面再来看下ExtensionFactory在ExtensionLoader中的具体应用:
public class ExtensionLoader<T> {
/**
* 扩展点获取工厂类,实际上是对ExtensionLoader功能的进一步封装
*/
private final ExtensionFactory objectFactory;
/**
* 在唯一的构造方法中,初始化objectFactory参数
* 使用的getAdaptiveExtension()方法获取默认实现类(也就是标注了@Adaptive的AdaptiveExtensionFactory的实例)
* @param type 接口type
*/
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
}
揭秘彩蛋:
相信结合原理以及具体的应用场景,现在对ExtensionFactory接口的原理应该是非常明了了
- Compiler之扩展点动态生产编译原理
在ExtensionLoader源码解析中讲到获取SPI接口自适应扩展点实例源码的时候,提到过在通过ExtensionLoader#getAdaptiveExtension()获取接口的默认实现时,如果这个接口没有使用@Adaptive注解标注的实现类,dubbo会根据原始接口来生产它的自适应实现类的Java代码(String),然后通过Compiler编译为class 。
这一动态生产SPI接口自适应实现类的方式是的扩展的自适应特性非常的灵活好用,而字节码编译器Compiler是实现这一特性的基础
首先看一下Compiler接口在Dubbo框架中的层次结构:
心细的同学可能会发现,Compiler接口采用了类似与ExtensionFactoty接口相同设计模式。具体业务的实现类是JdkCompiler和JavassistCompiler。 AdaptiveCompiler上标记了@Adaptive,表示其作为Compiler接口默认的实现类。负责整合调用JdkCompiler和JavassistCompiler
首先还是撸源码,先看一下Compiler接口的源码:
/**
* Compiler. (SPI, Singleton, ThreadSafe)
* 编译器SPI接口 ,由于在@SPI注解中指定了参数值为:javassist
* 表示Compiler使用javassist作为默认的实现
*/
@SPI("javassist")
public interface Compiler {
/**
* Compile java source code.
* 将java源文件编译为Class
* @param code Java source code
* @param classLoader classloader
* @return Compiled class
*/
Class<?> compile(String code, ClassLoader classLoader);
}
下图为Compile接口的配置文件:
再看一下Compiler接口唯一的一个抽象子类AbstractCompiler的源码,主要抽象的功能是从Java源码中获取类的全路径限定名
/**
* Abstract compiler. (SPI, Prototype, ThreadSafe)
* Compiler抽象类,主要抽象的功能是从Java源码中获取类的全路径限定名
*/
public abstract class AbstractCompiler implements Compiler {
private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s+([$_a-zA-Z][$_a-zA-Z0-9\\.]*);");
private static final Pattern CLASS_PATTERN = Pattern.compile("class\\s+([$_a-zA-Z][$_a-zA-Z0-9]*)\\s+");
@Override
public Class<?> compile(String code, ClassLoader classLoader) {
code = code.trim();
Matcher matcher = PACKAGE_PATTERN.matcher(code);
//获取Java源文件中定义的包名,值如:org.apache.dubbo.common.compiler.support
String pkg;
if (matcher.find()) {
pkg = matcher.group(1);
} else {
pkg = "";
}
matcher = CLASS_PATTERN.matcher(code);
//获取Java源文件中class的类名,值如:AbstractCompiler
String cls;
if (matcher.find()) {
cls = matcher.group(1);
} else {
throw new IllegalArgumentException("No such class name in " + code);
}
//拼接出class的全路径名,值如:org.apache.dubbo.common.compiler.support.AbstractCompiler
String className = pkg != null && pkg.length() > 0 ? pkg + "." + cls : cls;
try {
//通过class全路径名直接加载该class 。对于项目包中没有的类,这个方法会出错,然后走异常,这是重点
return Class.forName(className, true, org.apache.dubbo.common.utils.ClassUtils.getCallerClassLoader(getClass()));
} catch (ClassNotFoundException e) {
if (!code.endsWith("}")) {
throw new IllegalStateException("The java code not endsWith \"}\", code: \n" + code + "\n");
}
try {
/*
* 如果从项目中找不到该类,就执行doCompile
* 可以发现这个方法是一个模版方法,在本类中没有实现,留给它的子类取实现
* 也就是JavassistCompiler和JdkCompiler去实现
*/
return doCompile(className, code);
} catch (RuntimeException t) {
throw t;
} catch (Throwable t) {
throw new IllegalStateException("Failed to compile class, cause: " + t.getMessage() + ", class: " + className + ", code: \n" + code + "\n, stack: " + ClassUtils.toString(t));
}
}
}
protected abstract Class<?> doCompile(String name, String source) throws Throwable;
}
再看一下AbstractCompiler两个子类中是如何实现doCompile的
① Java实现(由于具体实现很复杂,这里只展示doCompile的实现步骤)
/**
* JdkCompiler. (SPI, Singleton, ThreadSafe)
*/
public class JdkCompiler extends AbstractCompiler {
private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
private final DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
private final ClassLoaderImpl classLoader;
private final JavaFileManagerImpl javaFileManager;
...
@Override
public Class<?> doCompile(String name, String sourceCode) throws Throwable {
int i = name.lastIndexOf('.');
String packageName = i < 0 ? "" : name.substring(0, i);
String className = i < 0 ? name : name.substring(i + 1);
JavaFileObjectImpl javaFileObject = new JavaFileObjectImpl(className, sourceCode);
javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH, packageName,
className + ClassUtils.JAVA_EXTENSION, javaFileObject);
Boolean result = compiler.getTask(null, javaFileManager, diagnosticCollector, options,
null, Arrays.asList(javaFileObject)).call();
if (result == null || !result) {
throw new IllegalStateException("Compilation failed. class: " + name + ", diagnostics: " + diagnosticCollector);
}
return classLoader.loadClass(name);
}
...
}
② Javassist实现
/**
* JavassistCompiler. (SPI, Singleton, ThreadSafe)
*/
public class JavassistCompiler extends AbstractCompiler {
private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\s+([\\w\\.\\*]+);\n");
private static final Pattern EXTENDS_PATTERN = Pattern.compile("\\s+extends\\s+([\\w\\.]+)[^\\{]*\\{\n");
private static final Pattern IMPLEMENTS_PATTERN = Pattern.compile("\\s+implements\\s+([\\w\\.]+)\\s*\\{\n");
private static final Pattern METHODS_PATTERN = Pattern.compile("\n(private|public|protected)\\s+");
private static final Pattern FIELD_PATTERN = Pattern.compile("[^\n]+=[^\n]+;");
@Override
public Class<?> doCompile(String name, String source) throws Throwable {
CtClassBuilder builder = new CtClassBuilder();
builder.setClassName(name);
// process imported classes
Matcher matcher = IMPORT_PATTERN.matcher(source);
while (matcher.find()) {
builder.addImports(matcher.group(1).trim());
}
// process extended super class
matcher = EXTENDS_PATTERN.matcher(source);
if (matcher.find()) {
builder.setSuperClassName(matcher.group(1).trim());
}
// process implemented interfaces
matcher = IMPLEMENTS_PATTERN.matcher(source);
if (matcher.find()) {
String[] ifaces = matcher.group(1).trim().split("\\,");
Arrays.stream(ifaces).forEach(i -> builder.addInterface(i.trim()));
}
// process constructors, fields, methods
String body = source.substring(source.indexOf('{') + 1, source.length() - 1);
String[] methods = METHODS_PATTERN.split(body);
String className = ClassUtils.getSimpleClassName(name);
Arrays.stream(methods).map(String::trim).filter(m -> !m.isEmpty()).forEach(method -> {
if (method.startsWith(className)) {
builder.addConstructor("public " + method);
} else if (FIELD_PATTERN.matcher(method).matches()) {
builder.addField("private " + method);
} else {
builder.addMethod("public " + method);
}
});
// compile
ClassLoader classLoader = org.apache.dubbo.common.utils.ClassUtils.getCallerClassLoader(getClass());
CtClass cls = builder.build(classLoader);
return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain());
}
}
由于技术层面原因,Java和Javassist两种方式将Java源文件编译为Class的具体实现细节这里就不再做相关介绍,想要了解的同学自己去它们官方网站学习。本博客的重点是讲述Dubbo框架的底层实现原理和流程。
接下来就是我们的官方指定的实现AdaptiveCompiler要登场了。我还是透过源码来看它是如何工作的
/**
* AdaptiveCompiler. (SPI, Singleton, ThreadSafe)
* 代码编译器的默认实现,用于管理JavassistCompiler和JdkCompiler 。 原理类似用AdaptiveExtensionFactory!
*/
@Adaptive
public class AdaptiveCompiler implements Compiler {
/**
* 默认编译器,在dubbo框架中提供了两种实现javassist和jdk
*/
private static volatile String DEFAULT_COMPILER;
/**
* 设置代码编译器
* 此方法会在ApplicationConfig中被调用
* @param compiler 代码编译器实例的名称
*/
public static void setDefaultCompiler(String compiler) {
DEFAULT_COMPILER = compiler;
}
@Override
public Class<?> compile(String code, ClassLoader classLoader) {
Compiler compiler;
//获取Compiler接口对应的ExtensionLoader实例
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
// copy reference
String name = DEFAULT_COMPILER;
//如果指定使用什么编译器就是用指定的,没有指定就获取默认的,默认为Javassist Compiler
if (name != null && name.length() > 0) {
compiler = loader.getExtension(name);
} else {
compiler = loader.getDefaultExtension();
}
//调用真正的编译器编译字符串格式的Java类
return compiler.compile(code, classLoader);
}
}
可以看到,获取这个编译器的实现还是使用ExtensionLoader#getExtension(name) 。由此可见,ExtensionLoader在Dubbo框架中的地位。想要深入了解Dubbo框架的同学,必须得熟悉ExtensionLoader的实现原理!!!这个坎必须得迈过去!!!
- DCL原理
DCL全称是Double Check Lock(双重校验锁),是在高并发环境中确保线程安全的一个重要的模式。多用于单例模式中。
先可以通过双重检验锁定DCL这一遍文章好好了解下 。
讲述DCL原理之前先说一下volatile 和 synchronized 的区别:
① synchronized 修饰方法或者代码块。它是强制加锁,保证读和写都有原子性,性能开销大。
② volatile 只能修饰成员属性,保证在读的时候对所有线程可见,而写就不保证了。适合用在一个线程写,多个线程读的情况。
DCL常见的用法如下:
public class LazySingletonDCL {
private volatile static LazySingletonDCL instance;
private LazySingletonDCL() {
}
public static LazySingletonDCL getInstance() {
if (instance == null) {
synchronized (LazySingletonDCL.class) {
if (instance == null) {
instance = new LazySingletonDCL();
}
}
}
return instance;
}
}
心细的同学会发现,dubbo框架源码中这种用法无处不在。如在ExtensionLoader#getExtension(name)中
/**
* Find the extension with the given name.
* If the specified name is not found, then {@link IllegalStateException} will be thrown.
*
* 扩展类加载器中最核心的方法:
* 根据扩展点的名称返回扩展点的实现类
*/
@SuppressWarnings("unchecked")
public T getExtension(String name) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
//如果扩展点名称为true字符串,则返回默认的扩展类
if ("true".equals(name)) {
return getDefaultExtension();
}
/*
*扩展容器,从缓存中获取,没有就新建一个加入缓存
*/
final Holder<Object> holder = getOrCreateHolder(name);
//典型的DCL单例模式
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
//如果Holder是一个空的Holder时,就新建一个给定扩展名的扩展点实例
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
/**
* Helper Class for hold a value.
*/
public class Holder<T> {
private volatile T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
想要深入理解DCL保证线程安全的原理,需要熟悉Java的内存模型。这个现在已经有很多大牛的博客中有详细讲述到,我现在就不在赘述