概述
dubbo是可扩展的,让整个框架的接口和具体实现完全解偶。
1,java SPI
Java的扩展机制,SPI采用的是策略模式,一个接口可以多个实现类,我这里就写了一个
首先写个接口和接口实现类
//接口
public interface PrintService {
void printInfo();
}
//接口实现类
public class PrintServiceImpl implements PrintService {
@Override
public void printInfo() {
System.out.println("hello world");
}
}
然后main方法准备起来
/**
* java的SPI
* @param args
*/
public static void main(String[] args) {
ServiceLoader<PrintService> loader = ServiceLoader.load(PrintService.class);
//循环调用方法,因为一个接口可以多个实现类
for (PrintService p : loader) {
p.printInfo();
}
}
ServiceLoader这个类是在java.util中,这个就是javaSPI的核心,源码:java.util.ServiceLoader;自己看哈
当然还差一个东西,看了源码你会发现,ServiceLoader会去读取一个配置文件的:
private static final String PREFIX = "META-INF/services/";
所以在resoources里新建文件夹META-INF/services/,在文件夹里新建一个文件,文件名是你接口的全路径:
com.dubbo.study.utils.test.PrintService
这个文件里的内容是对应的实现类:com.dubbo.study.utils.test.PrintServiceImpl
然后执行main方法,就会调用接口啦,打印出hello world
总结:看过程是不是很熟悉,很想调用接口,这跟我们平时用spring的调用service接口,spring是将接口加载为一个bean,然后注入,这里其实也差不多,通过META-INF/services/文件里的配置,然后用ServiceLoader去加载接口,当然SPI作用是用于java的扩展插件,
2,dubbo SPI
dubbo也是为了可以扩展,灵活性高,采用了javaSPI的思想,当然做了一些改进,
小例子:
首先还是接口与接口实现类,实现类不用改,接口里加个注解:
@SPI("print")//加了一个SPI注解,这是dubbo的
public interface PrintService {
void printInfo();
}
resources里的META-INF新建dubbo文件夹,看上面图,com.dubbo.study.utils.test.PrintService文件里内容是:
print=com.dubbo.study.utils.test.PrintServiceImpl
print对应的就是注解里的内容。
然后执行
/**
* dubbo的SPI
* @param args
*/
public static void main(String[] args) {
PrintService p = ExtensionLoader.getExtensionLoader(PrintService.class).getDefaultExtension();
p.printInfo();
}
也可以调用到接口,打印出hello world
这里dubbo的ExtensionLoader类,这个类的源码在org.apache.dubbo.common.extension.ExtensionLoader,
在源码里可以看到
private static final String SERVICES_DIRECTORY = "META-INF/services/";
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
private static final String DUBBO_INTERNAL_DIRECTORY = "META-INF/dubbo/internal/";
在META-INF里只要有这三个文件夹,把接口的配置文件放在这三个文件夹里都可以。
配置文件的内容采用key=value形式。
dubboSPI可以分为class缓存,实例缓存。
private final ExtensionFactory objectFactory;
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap();
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder();
private final Map<String, Object> cachedActivates = new ConcurrentHashMap();
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap();
private final Holder<Object> cachedAdaptiveInstance = new Holder();
扩展点的特性:自动包装,自动加载,自适应,自动激活
当然这些特性除了@SPI注解是不够的,还要有@Adaptive(自适应),@Activate(自动激活)。
@SPi刚刚已经演示过用法了
@Adaptive
@SPI("print")
public interface PrintService {
@Adaptive({"print",""})
void printInfo();
}
注解里值是配置文件对应的key,可以配置多个。
源码在和ExtensionLoader一样的包里,可以自己去多看看
@Activate
可以标记在方法,类,接口等等,
3,ExtensionLoader源码解析
3.1 getExtension(String name) 是整个扩展加载器中最核心的方法,源码表示看不大懂,哈哈哈哈哈
public T getExtension(String name) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
} else if ("true".equals(name)) {
//如果传入true,就加载默认扩展类
return this.getDefaultExtension();
} else {
//判断缓存里有没有
Holder<Object> holder = this.getOrCreateHolder(name);
Object instance = holder.get();
if (instance == null) {
synchronized(holder) {
instance = holder.get();
if (instance == null) {
//没有的话就新建
instance = this.createExtension(name);
holder.set(instance);
}
}
}
return instance;
}
}
//判断缓存里有没有
private Holder<Object> getOrCreateHolder(String name) {
Holder<Object> holder = (Holder)this.cachedInstances.get(name);
if (holder == null) {
this.cachedInstances.putIfAbsent(name, new Holder());
holder = (Holder)this.cachedInstances.get(name);
}
return holder;
}
private T createExtension(String name) {
Class<?> clazz = (Class)this.getExtensionClasses().get(name);
if (clazz == null) {
throw this.findException(name);
} else {
try {
T instance = EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = EXTENSION_INSTANCES.get(clazz);
}
this.injectExtension(instance);
Set<Class<?>> wrapperClasses = this.cachedWrapperClasses;
Class wrapperClass;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for(Iterator var5 = wrapperClasses.iterator(); var5.hasNext(); instance = this.injectExtension(wrapperClass.getConstructor(this.type).newInstance(instance))) {
wrapperClass = (Class)var5.next();
}
}
return instance;
} catch (Throwable var7) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " + this.type + ") couldn't be instantiated: " + var7.getMessage(), var7);
}
}
}
//这个方法去加载
private T injectExtension(T instance) {
try {
if (this.objectFactory != null) {
Method[] var2 = instance.getClass().getMethods();
int var3 = var2.length;
for(int var4 = 0; var4 < var3; ++var4) {
Method method = var2[var4];
if (this.isSetter(method) && method.getAnnotation(DisableInject.class) == null) {
Class<?> pt = method.getParameterTypes()[0];
if (!ReflectUtils.isPrimitives(pt)) {
try {
String property = this.getSetterProperty(method);
Object object = this.objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception var9) {
logger.error("Failed to inject via method " + method.getName() + " of interface " + this.type.getName() + ": " + var9.getMessage(), var9);
}
}
}
}
}
} catch (Exception var10) {
logger.error(var10.getMessage(), var10);
}
return instance;
}
实现了一个完整的普通扩展类加载过程,加载的每一步,都会先检查缓存中是否已经存在所需要的数据,如果存在就直接返回,没有就加载,初始化分4步:
1)读取SPI对应路径下的配置,并加载缓存
2)根据传入的名称初始化对应的扩展类
3)尝试查找符合条件的包装类
4)返回对应的扩展类实例
3.2 getAdaptiveExtension
public T getAdaptiveExtension() {
Object instance = this.cachedAdaptiveInstance.get();
if (instance == null) {
if (this.createAdaptiveInstanceError != null) {
throw new IllegalStateException("Failed to create adaptive instance: " + this.createAdaptiveInstanceError.toString(), this.createAdaptiveInstanceError);
}
synchronized(this.cachedAdaptiveInstance) {
instance = this.cachedAdaptiveInstance.get();
if (instance == null) {
try {
instance = this.createAdaptiveExtension();
this.cachedAdaptiveInstance.set(instance);
} catch (Throwable var5) {
this.createAdaptiveInstanceError = var5;
throw new IllegalStateException("Failed to create adaptive instance: " + var5.toString(), var5);
}
}
}
}
return instance;
}
在这个方法中,会为扩展点接口自动生成实现类字符串,为接口中每个有@Adaptive注解的方法生成默认实现,每个默认实现都会从url中提取adaptive参数值,并以此为依据动态加载扩展点,然后根据不同的编译器,把实现类字符串编译为自适应类并返回。
4,ExtensionFactory原理
ExtensionLoader是核心,那ExtensionLoader是怎么加载的呢,答案是通过ExtensionFactory工厂创建的,
//工厂接口
@SPI
public interface ExtensionFactory {
<T> T getExtension(Class<T> var1, String var2);
}
//AdaptiveExtension实现类,默认实现。
//除了这个还有另外SpiExtensionFactory和springExtensionFactory工厂。
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList();
Iterator var3 = loader.getSupportedExtensions().iterator();
while(var3.hasNext()) {
String name = (String)var3.next();
list.add(loader.getExtension(name));
}
this.factories = Collections.unmodifiableList(list);
}
public <T> T getExtension(Class<T> type, String name) {
Iterator var3 = this.factories.iterator();
Object extension;
do {
if (!var3.hasNext()) {
return null;
}
ExtensionFactory factory = (ExtensionFactory)var3.next();
extension = factory.getExtension(type, name);
} while(extension == null);
return extension;
}
}
//SpiExtensionFactory工厂
public class SpiExtensionFactory implements ExtensionFactory {
public SpiExtensionFactory() {
}
public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
if (!loader.getSupportedExtensions().isEmpty()) {
//可以看到最终还是回答AdaptiveExtension
return loader.getAdaptiveExtension();
}
}
return null;
}
}
5,动态编译
三种编译器,jdk编译器,javassist编译器和AdaptiveCompiler编译器
AdaptiveCompiler编译器:
package org.apache.dubbo.common.compiler.support;
import org.apache.dubbo.common.compiler.Compiler;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.ExtensionLoader;
@Adaptive
public class AdaptiveCompiler implements Compiler {
private static volatile String DEFAULT_COMPILER;
public AdaptiveCompiler() {
}
//设置默认编译器名称
public static void setDefaultCompiler(String compiler) {
DEFAULT_COMPILER = compiler;
}
public Class<?> compile(String code, ClassLoader classLoader) {
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
String name = DEFAULT_COMPILER;
Compiler compiler;
if (name != null && name.length() > 0) {
compiler = (Compiler)loader.getExtension(name);
} else {
compiler = (Compiler)loader.getDefaultExtension();
}
//通过ExtensionLoader获取对应的编译器扩展实现,并调用真正的compile进行编译
return compiler.compile(code, classLoader);
}
}
JavassistCompiler编译器
package org.apache.dubbo.common.compiler.support;
import java.util.Arrays;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javassist.CtClass;
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]+;");
public JavassistCompiler() {
}
//前面一系列操作都是用正则匹配文件的import,类名,方法名,等等,
public Class<?> doCompile(String name, String source) throws Throwable {
CtClassBuilder builder = new CtClassBuilder();
builder.setClassName(name);
Matcher matcher = IMPORT_PATTERN.matcher(source);
while(matcher.find()) {
builder.addImports(matcher.group(1).trim());
}
matcher = EXTENDS_PATTERN.matcher(source);
if (matcher.find()) {
builder.setSuperClassName(matcher.group(1).trim());
}
matcher = IMPLEMENTS_PATTERN.matcher(source);
if (matcher.find()) {
String[] ifaces = matcher.group(1).trim().split("\,");
Arrays.stream(ifaces).forEach((i) -> {
builder.addInterface(i.trim());
});
}
String body = source.substring(source.indexOf(123) + 1, source.length() - 1);
String[] methods = METHODS_PATTERN.split(body);
String className = ClassUtils.getSimpleClassName(name);
Arrays.stream(methods).map(String::trim).filter((m) -> {
return !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);
}
});
//最后去加载这个类
ClassLoader classLoader = org.apache.dubbo.common.utils.ClassUtils.getCallerClassLoader(this.getClass());
CtClass cls = builder.build(classLoader);
return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain());
}
}
jdk编译器
jdk编译器就复杂了,位置跟javassist一样,自己看把,
最后
以上就是大胆星星为你收集整理的学习dubbo(3):dubbo扩展点加载机制的全部内容,希望文章能够帮你解决学习dubbo(3):dubbo扩展点加载机制所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复