介绍
- API:应用程序接口【Application Programming Interface】
- SPI:服务提供接口【Service Provider Interface】
- Java本身提供了SPI机制,常见的如mysql驱动
JDK 的 SPI演示
测试类
public static void main(String[] args) {
ServiceLoader<SPIService> load = ServiceLoader.load(SPIService.class);
for (SPIService spiService : load) {
System.out.println(spiService.sayHello("tom"));
}
}
定义接口
public interface SPIService {
String sayHello(String name);
}
接口实现一
public class SPIServiceImpl01 implements SPIService {
@Override
public String sayHello(String name) {
return "SPIServiceImpl01 : hello " + name;
}
}
接口实现二
public class SPIServiceImpl02 implements SPIService {
@Override
public String sayHello(String name) {
return "SPIServiceImpl02 : hello " + name;
}
}
META-INF/services/com.jiangzheng.course.dubbo.consumer.spi.service.SPIService文件内容为:
com.jiangzheng.course.dubbo.consumer.spi.impl.SPIServiceImpl01
com.jiangzheng.course.dubbo.consumer.spi.impl.SPIServiceImpl02
测试类输出
SPIServiceImpl01 : hello tom
SPIServiceImpl02 : hello tom
Dubbo 的 SPI
介绍
- Apache Dubbo自主实现了SPI机制
- Apache Dubbo的SPI提高了加载速度和资源消耗
- Apache Dubbo 为SPI提供了IOC和AOP的支持
源码
使用方式:Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
接口有多个实现类,一种方法有多个实现,有时候需要根据不同的入参来决定调用不同的方法实现,@Adaptive就是实现了这样一种功能。
入口:通过调用ExtensionLoader的getAdaptiveExtension()方法,获得一个动态代理类,这个动态代理类能够根据入参URL中的key-value来判断调用哪一个实现方法。非cglib和反射实现。
先看getAdaptiveExtension(),先从缓存中取,看看之前有没有创建过,如果没有就调用createAdaptiveExtension(),层层深入,看createAdaptiveExtension()在做啥
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()就两个动作,一是获取class,二是实例化。就这?继续深入getAdaptiveExtensionClass()方法。
private T createAdaptiveExtension() {
try {
T t = (T) getAdaptiveExtensionClass().newInstance();
return injectExtension(t);
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
getAdaptiveExtensionClass()方法如下,貌似也没提供多少有用的信息,接着深入createAdaptiveExtensionClass()方法。
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
createAdaptiveExtensionClass()方法如下,首先new了一个AdaptiveClassCodeGenerator,根据字面意思理解,代码生成器??再看最后一行Compiler,大概能猜到它的实现原理是这样的:首先用将一个类写成String,再由Compiler编译器去编译,然后返回编译出来的class信息。进入generate()方法验证一下
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);
}
果不其然,通过StringBuilder写了一个类!基于编辑字节码的动态代理!
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();
}
其中,注意上面for循环中generateMethod方法,如果被生成的方法上没有注解@Adaptive,就会跑出Unsupported异常
private String generateMethodContent(Method method) {
Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
StringBuilder code = new StringBuilder(512);
if (adaptiveAnnotation == null) {
return generateUnsupported(method);
} else {
......
}
}
再看看最后生成的代码,为了方便阅读,我加了一些换行和空格
package org.apache.dubbo.registry.zookeeper;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Fruit$Adaptive implements org.apache.dubbo.registry.zookeeper.Fruit {
public void printName(org.apache.dubbo.common.URL arg0) {
if (arg0 == null)
throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg0;
String extName = url.getParameter("key1", "apple");
if(extName == null)
throw new IllegalStateException("Failed to get extension (org.apache.dubbo.registry.zookeeper.Fruit) name from url (" + url.toString() + ") use keys([key1])");
org.apache.dubbo.registry.zookeeper.Fruit extension
= (org.apache.dubbo.registry.zookeeper.Fruit)ExtensionLoader
.getExtensionLoader(org.apache.dubbo.registry.zookeeper.Fruit.class)
.getExtension(extName);
extension.printName(arg0);
}
}
然后用编译器将上面的代码编译,就可以得到Class信息了,然后反射实例化就行了。另外,dubbo有三种编译器,jdk编译器,Javassist编译器和AdaptiveCompiler编译器,默认是Javassist编译器,但是在这里用的是AdaptiveCompiler编译器。
详细讲解:https://www.cnblogs.com/huan30/p/12769683.html
|