概述
文章目录
- 前言
- 一、关于dubbo spi
- 1、为什么dubbo没有采用java的SPI?
- 2、dubbo spi的改进
- 3、dubbo spi的约定
- 二、小例子
- 1.创建接口
- 2.创建实现类
- 3、创建配置文件
- 4、使用dubbo spi加载实现类
- 三、原理分析
- 1、我们以`ServiceConfig`类的`protocol ` 对象为例
- 2、`getExtensionLoader(Class type)`
- 3、`getAdaptiveExtension()`
- 4、`getExtension(String name)`
前言
我们之前在《JAVA SPI 机制》中了解了java自带的SPI功能,而dubbo也实现了一套自己的SPI机制。
一、关于dubbo spi
1、为什么dubbo没有采用java的SPI?
因为java的SPI加载实现类时,只能通过迭代器遍历的方式获取加载的实现类。
就算ServiceLoader
有延迟加载的功能,那如果先加载的是我们不需要的类,ServiceLoader
仍会对其实例化,这就造成了一种资源的浪费;
如果加载类时,该类的初始化时间很长,那我们的程序还要等待该类加载完后再执行,就会影响我们程序的性能。
由于ServiceLoader
的内部类LazyIterator
并没有提供锁机制来保证线程安全,所以多线程使用ServiceLoader对象时,会存在线程安全的问题。
2、dubbo spi的改进
- dubbo spi提供了对扩展点的IoC和AOP的支持,一个扩展点可以直接setter进其他的扩展点
- dubbo spi可以根据接口的@SPI的内容加载对应的是实现类
3、dubbo spi的约定
- SPI文件可以存放在classpath下的
META-INF/dubbo/internal/、META-INF/dubbo/、META-INF/services/
3个目录的任意一个目录,并且文件名为接口的全限定性类名。 - SPI文件内容格式为,扩展名=具体的类名,如
cat=com.wgc.dubbo.spi.impl.Cat
- 在使用时,需要在接口上增加@SPI注解
二、小例子
1.创建接口
代码如下(示例):
package com.wgc.dubbo.spi;
import com.alibaba.dubbo.common.extension.SPI;
@SPI
public interface Animal {
/**
* 输出动物叫声
*/
void say();
}
2.创建实现类
代码如下(示例):
package com.wgc.dubbo.spi.impl;
import com.wgc.dubbo.spi.Animal;
public class Dog implements Animal {
@Override
public void say() {
System.out.println("汪汪");
}
}
package com.wgc.dubbo.spi.impl;
import com.wgc.dubbo.spi.Animal;
public class Cat implements Animal {
@Override
public void say() {
System.out.println("喵喵");
}
}
3、创建配置文件
文件内容为:
cat=com.wgc.dubbo.spi.impl.Cat
dog=com.wgc.dubbo.spi.impl.Dog
4、使用dubbo spi加载实现类
package com.wgc.dubbo.spi;
import java.sql.SQLException;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class SpiMainClass {
public static void main(String[] args) throws SQLException {
Animal cat = ExtensionLoader.getExtensionLoader(Animal.class).getExtension("cat");
cat.say();
System.out.println("---------------------");
Animal dog = ExtensionLoader.getExtensionLoader(Animal.class).getExtension("dog");
dog.say();
}
}
上面代码运行结果为:
喵喵
---------------------
汪汪
三、原理分析
dubbo spi中获取对象都是通过ExtensionLoader<T>
类的 getExtension(String name)
方法实现的。
其实现路径为:
getExtensionLoader(Class<T> type)
该接口new 了一个ExtensionLoader
,并将其缓存getAdaptiveExtension()
获取一个扩展装饰类的对象,我们通过这个扩展装饰类来根据@SPI的值选取具体的实现类getExtension(String name)
根据传入的参数name,获取SPI配置中对应的实现类对象
下面介绍下这3个方法(只留下了源码中重要的部分)
1、我们以ServiceConfig
类的protocol
对象为例
源码如下:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;
/**
* Protocol. (API/SPI, Singleton, ThreadSafe)
*/
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy();
}
我们在来看下dubbo工程中classpath下所有protocol
对应的SPI文件的内容:
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
2、getExtensionLoader(Class<T> type)
- 验证type上是否有@SPI注解
- 从缓存EXTENSION_LOADERS中获取type对应的ExtensionLoader
- 如果缓存中不存在,就new ExtensionLoader(type)并将其放入缓存
源码如下:
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
// ...此处省略了一些简单的验证代码...
// 如果接口上没有@SPI注解就抛出异常
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
// EXTENSION_LOADERS是一个ConcurrentMap<Class<?>, ExtensionLoader<?>>类型的成员变量,用来缓存ExtensionLoader对象
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
第一次执行上面的方法时,缓存中并没有存放ExtensionLoader对象,所以会执行new ExtensionLoader(type)方法。
ExtensionLoader(type)代码如下:
private ExtensionLoader(Class<?> type) {
this.type = type;
// objectFactory工厂模式
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
可以看到objectFactory = ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
而ExtensionLoader.getExtensionLoader(ExtensionFactory.class)
也会执行new ExtensionLoader<T>(type)
这行代码来获取type=ExtensionFactory.class
的ExtensionLoader
对象,此时ExtensionFactory
的ExtensionLoader
对象的属性为:
type=ExtensionFactory.class;
objectFactory=null;
这几步功能如下图所示:
获取到ExtensionFactory
的ExtensionLoader
对象后,会执行getAdaptiveExtension()
方法,该方法最终返回的结果就是我们调用getExtensionLoader(Protocol.class)
方法返回对象的objectFactory
的值,为AdaptiveExtensionFactory[SpiExtensionFactory, SpringExtensionFactory]
对象。
3、getAdaptiveExtension()
该方法的作用是:
获取一个扩展装饰类的对象。这个类有一个规则:如果@Adaptive注解在类上,就获取一个装饰类(由类本身实现),如果@Adaptive注解在方法上,就获取一个动态代理类(由代码模板生成)例如:Protocol$Adaptive对象。
- 1.
getAdaptiveExtension()
1)从缓存对象cachedAdaptiveInstance中获取装饰类
2)如果缓存中不存在,则调用createAdaptiveExtension()获取装饰类,并将其放入缓存中
public T getAdaptiveExtension() {
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
// 双重检查,保证线程安全
if (instance == null) {
try {
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
}
}
}
} else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
第一次执行getAdaptiveExtension()
时,cachedAdaptiveInstance
中不包含目标类,所以会执行createAdaptiveExtension()
方法
- 2.
createAdaptiveExtension()
1)生成装饰类/动态代理类对象
2)通过injectExtension方法为装饰类对象注入依赖属性值(IoC的setter注入原理)
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
- 3.
createAdaptiveExtension()
又执行了两个方法injectExtension()
方法和getAdaptiveExtensionClass()
方法 - 3.1我们先看下
getAdaptiveExtensionClass()
1)调用getExtensionClasses()为ExtensionLoader类的成员变量赋值
2)如果在SPI实现类中有包含@Adaptive注解的类,那么该类就是我们要的装饰类,直接返回就可以了
3)如果没有包含@Adaptive注解的SPI实现类,就由系统生成一个动态代理类
/**
* 获取泛型T自适应类对象的class对象:
*/
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
// cachedAdaptiveClass存的是通过SPI取出的包含@Adaptive注解类,这个类是人工实现的装饰类,如果存在的话,就不需要有dubbo编译出一个动态代理类了,所以直接返回就行了
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 走到这一步,说明没有包含@Adaptive注解的人工实现装饰类,这就需要由系统生成一个动态代理类
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
- 3.1.1
getExtensionClasses()
1)从缓存中获取扩展装饰类
2)如果缓存中不存在,则执行loadExtensionClasses();
方法获取
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;
}
- 3.1.2
loadExtensionClasses()
1)解析type变量的@SPI 注解,如果有值,将其值放入成员变量cachedDefaultName中
2)调用loadDirectory
方法加载指定文件夹中的配置文件
private Map<String, Class<?>> loadExtensionClasses() {
// 获取 SPI 注解,这里的 type 变量是在调用 getExtensionLoader 方法时传入的
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
// 对 SPI 注解内容进行切分
String[] names = NAME_SEPARATOR.split(value);
// 检测 SPI 注解内容是否合法,不合法则抛出异常
if (names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
// 对于protocol类来说,names[0]=“dubbo”
if (names.length == 1)
// 将@SPI(value)的value值存放在成员变量cachedDefaultName 中
cachedDefaultName = names[0];
}
}
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
// 加载指定文件夹下的配置文件
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
通过本方法调用了3个loadDirectory()
方法,大家应该就明白我们之前在dubbo SPI约束中说的 SPI配置文件的路径是META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/这3个路径
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 = DUBBO_DIRECTORY + "internal/";
- 3.1.3
loadDirectory(Map<String, Class<?>> extensionClasses, String dir)
1)生成SPI配置文件路径
2)获取当前类的类加载器
3)通过类加载器扫描classpath目录下的spi配置文件
4)循环遍历扫描到的配置文件,并加载其中的类
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
// fileName = dubbo spi扫描文件夹路径 + type 全限定性类名
String fileName = dir + type.getName();
try {
Enumeration<java.net.URL> urls;
// 获取ExtensionLoader的类加载器
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 when load extension class(interface: " +
type + ", description file: " + fileName + ").", t);
}
}
- 3.1.4
loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL)
1)扫描SPI配置文件的每一行,并将其key,value分隔开
2)调用loadClass
方法将加载的value类放入到合适的缓存变量中
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
try {
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) {// AdaptiveCompiler JdkCompiler JavassistCompiler
// 加载类,并通过 loadClass 方法对类进行缓存
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);
}
}
}
} finally {
reader.close();
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}
- 3.1.5
loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name)
1)如果clazz中包含@Adaptive注解,将执行赋值操作cachedAdaptiveClass = clazz;
2)如果clazz中不包含@Adaptive注解,并且构造函数的参数为目标接口(type)类型时:将clazz保存到全局变量的cachedWrapperClasses中;cachedWrapperClasses存放的是在getExtension(String name)的方法中使用AOP的装饰类
3)如果不满足上面两个条件,进行判断,如果clazz上有@Activate注解时,将clazz作为key存放到全局变量cachedActivates中;
4)如果不满足条件1和条件2,则将clazz作为value存入到参数extensionClasses中
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 when load extension class(interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + "is not subtype of interface.");
}
// 如果class中有@Adaptive注解
if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (!cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
} else if (isWrapperClass(clazz)) {
// 如果class无@Adaptive注解,并且构造函数的参数为目标接口(type)类型时
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} else {
clazz.getConstructor();
if (name == null || name.length() == 0) {
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);
if (names != null && names.length > 0) {
// 获取该类中存在的@Activate注解,如果注解不存在,则返回null
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
}
最终在我们加载完Protocol
的所有SPI实现类后,这些类存储情况如下所示:
cachedAdaptiveClass= null
cachedWrapperClasses=[
class com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper,
class com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
]
cachedActivates= {}
cachedNames = {
class com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol=injvm,
class com.alibaba.dubbo.registry.integration.RegistryProtocol=registry,
class com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol=dubbo,
class com.alibaba.dubbo.rpc.support.MockProtocol=mock
}
而loadClass
方法的参数对象extensionClasses
存放内容如下:
{
registry=class com.alibaba.dubbo.registry.integration.RegistryProtocol,
injvm=class com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol,
dubbo=class com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol,
mock=class com.alibaba.dubbo.rpc.support.MockProtocol
}
执行完这个方法后,就会一路返回,直到3.1的getAdaptiveExtensionClass()
方法,因为cachedAdaptiveClass
对象值为null
,所以会执行createAdaptiveExtensionClass()
方法
- 3.1.2
createAdaptiveExtensionClass()
走到这个方法,说明SPI加载的实现类中都没有在类上增加@Adaptive注解,所以需要自动生成和编译一个adaptive动态类
1)使用createAdaptiveExtensionClassCode()方法,动态生成java代码
2)根据SPI加载Compiler的实现类
3)根据compiler编译出动态生成的java代码的class对象
private Class<?> createAdaptiveExtensionClass() {
// 根据模板生成动态了代码
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
// compiler有两个实现类:AdaptiveCompiler和AbstractCompiler,通过getAdaptiveExtension方法获取的是AdaptiveCompiler
Compiler compiler = ExtensionLoader.getExtensionLoader(Compiler.class).getAdaptiveExtension();
// 编译生成动态类的class对象
return compiler.compile(code, classLoader);
}
createAdaptiveExtensionClassCode()生成的Protocol接口的动态类代码如下所示:
根据规则,接口中如果方法上没有@Adaptive注解,那么动态生成的方法体为抛出一个异常
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
// 根据规则,Protocol 接口的destroy()方法上没有@Adaptive注解,所以生成的代码方法体为抛出一个异常
public void destroy() {
throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
// 根据规则,Protocol 接口的getDefaultPort()方法上没有@Adaptive注解,所以生成的代码方法体为抛出一个异常
public int getDefaultPort() {
throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
if (arg1 == null)
throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
}
- 3.2
injectExtension(T instance)
injectExtension()方法在Protocol获取装饰类时,没有起作用。我们在下面getExtension(String name)
中进行讲解
4、getExtension(String name)
getExtension(name)获取查找具有给定名称的扩展类。如果未找到指定的名称,则抛出IllegalStateException异常。这里会根据条件进行IOC和AOP来获取对象
- 1
getExtension(String name)
1)判断参数name是否合法,那么为@SPI注解的内容
2)从缓存中获取扩展类
3)如果缓存中没有,则调用createExtension(name)方法生成
public T getExtension(String name) { // javassist dubbo javassist injvm
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
return getDefaultExtension();
}
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
- 2
createExtension(String name)
1) 先调用getExtensionClasses()方法获取name对应的class对象,这个方法我们在之前讲过
2)根据class对象从缓存中取出实例对象
3)调用injectExtension方法使用IoC注入对象属性值
4)循环装饰类cachedWrapperClasses(这个对象我们之前讲过),采用了装饰者模式增强目标对象(AOP)。
private T createExtension(String name) {
// 根据SPI配置文件的key获得value对应的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);
}
// 使用IOC注入对象
injectExtension(instance);
// cachedWrapperClasses放的都是class无adaptive注解,并且构造函数的参数为目标接口(type)类型,用来做AOP
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
// 通过装饰者模式,使用AOP包装instance对象
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 + ") could not be instantiated: " + t.getMessage(), t);
}
}
- 3
injectExtension(T instance)
Dubbo IOC 是通过 setter 方法注入依赖。
1)通过反射获取到实例的所有方法,然后再遍历方法列表,检测方法名是否具有 setter 方法特征
2) 如果存在满足条件的setter方法,则通过 ObjectFactory 获取依赖对象,
3)通过反射调用 setter 方法将依赖设置到目标对象中。
整个过程对应的代码如下:
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
// 遍历目标类的所有方法
for (Method method : instance.getClass().getMethods()) {
// 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
// 获取 setter 方法参数类型
Class<?> pt = method.getParameterTypes()[0];// 方法返回一个Class对象数组,它们以声明顺序表示由此Method对象表示的方法的形式参数类型
try {
// 获取属性名,比如 setProtocol 那么property = “protocol”
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
// 从 ObjectFactory 中获取依赖对象
Object object = objectFactory.getExtension(pt, property);// pt:参数类型
if (object != null) {
// 通过反射调用 setter 方法设置依赖
method.invoke(instance, object);// instance:需要执行method的对象,object:method方法的参数,
}
} catch (Exception e) {
logger.error("fail to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
最后
以上就是内向鱼为你收集整理的dubbo SPI 的实现前言一、关于dubbo spi二、小例子三、原理分析的全部内容,希望文章能够帮你解决dubbo SPI 的实现前言一、关于dubbo spi二、小例子三、原理分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复