在上一篇:SPI机制(一)中研究了Dubbo SPI的自适应原理;SPI机制(二)中我们来研究下Dubbo SPI是如何实现IOC的。
1. 起点:测试用例
我们还是从测试用例来开始分析。
- SPI 接口
@SPI("injection")
public interface InjectExt {
String echo(String msg);
}
- 接口实现(为了简洁代码,删除了get方法)
public class InjectExtImpl implements InjectExt {
private SimpleExt simpleExt;
private SimpleExt simpleExt1;
private Object genericType;
public void setSimpleExt(SimpleExt simpleExt) {
this.simpleExt = simpleExt;
}
// @DisableInject
public void setSimpleExt1(SimpleExt simpleExt1) {
this.simpleExt1 = simpleExt1;
}
public void setGenericType(Object genericType) {
this.genericType = genericType;
}
}
- 测试用例
{
InjectExt injectExt = ExtensionLoader.getExtensionLoader(InjectExt.class).getExtension("injection");
InjectExtImpl injectExtImpl = (InjectExtImpl) injectExt;
Assertions.assertNotNull(injectExtImpl.getSimpleExt());
Assertions.assertNull(injectExtImpl.getSimpleExt1());
Assertions.assertNull(injectExtImpl.getGenericType());
}
2. getExtension
getExtensionLoader在SPI机制(一)中有分析,就是拿到与InjectExt相关的ExtensionLoader,这里就不赘述了。直接来到getExtension方法。
public T getExtension(String name) {
//校验name
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
if ("true".equals(name)) {
return getDefaultExtension();
}
...
instance = createExtension(name);
return instance;
}
3. createExtension
private T createExtension(String name) {
//找到name对应的类;getExtensionClasses在上一章已经分析过,就是去load spi 配置文件,将key-value(Class<?>) 存进map中。
Class<?> clazz = getExtensionClasses().get(name);
// 根据clazz 创建instance :省略
...
// 为创建的实例,注入属性值
injectExtension(instance);
...
return instance;
}
4. injectExtension
根据类里的set方法,来加载其他的自适应类,然后通过反射的方式给属性附值。
private T injectExtension(T instance) {
if (objectFactory != null) {
// 获取类下面的所有方法
for (Method method : instance.getClass().getMethods()) {
if (isSetter(method)) {//判断是否是set方法
//如果set方法被标注为DisableInject,则跳过自动注入
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
// 判断set方法的参数,如果是基本类型,跳过自动注入。
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
//获取set方法参数里的自适应类,并通过反射的方式调用set方法,完成属性的注入
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
}
}
}
}
5. objectFactory
我们进入objectFactory的getExtension方法,看它是如何创建属性的自适应类。
- a. getExtension
public <T> T getExtension(Class<T> type, String name) {
// 这里的factories是在spi文件中,指定的所有的ExtensionFactory的实现类
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
// 找到了实现类,就立即返回
if (extension != null) {
return extension;
}
}
return null;
}
这里的factories 是在创建AdaptiveExtensionFactory的时候,初始化的
public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
}
再来看看getSupportedExtensions方法
public Set<String> getSupportedExtensions() {
Map<String, Class<?>> clazzes = getExtensionClasses();
return Collections.unmodifiableSet(new TreeSet<>(clazzes.keySet()));
}
在前面的分析中,我们知道getExtensionClasses只会去保存那些没有被Adaptive标记的实现类。在ExtensionFactory的SPI 配置文件中,只定义了两个
ExtensionFactory 类:
adaptive=org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory
由于AdaptiveExtensionFactory被Adaptive标记,那么在getAdaptiveExtention的时候会默认返回AdaptiveExtensionFactory;另外的SpiExtensionFactory会保存factories中。
- b. SpiExtensionFactory.getExtention
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()) {
return loader.getAdaptiveExtension();
}
}
return null;
}
最后,通过getAdaptiveExtension生成SimpleExt的自适应类。
6. 回到injectExtension方法
在injectionExtension方法中,生成了自适应的Object,然后通过反射的方式,将自适应的SimpleExt注入到InjectExtImpl中。
注意,这里只是注入了自适应类,真正的实现要在SimpleExt属性值调用方法时,才确定。
7. 测试
- 测试1: 在一个非Adaptive类里面注入Adaptive类
注意使用的是getExtension("injection")
{
InjectExt injectExt = ExtensionLoader.getExtensionLoader(InjectExt.class).getExtension("injection");
InjectExtImpl injectExtImpl = (InjectExtImpl) injectExt;
Map<String, String> map = new HashMap<String, String>();
map.put("simple.ext", "impl2");
URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
String echo = injectExtImpl.getSimpleExt().echo(url, "haha");
assertEquals("Ext1Impl2-echo", echo);
}
- 测试2:在一个Adaptive类里面注入Adaptive类
!!!不行!!!。所以还是老实用getExtension而不是getAdaptiveExtension。
因为在自动注入的原理中,要调用setXXX方法。如果要想在Adaptive类中使用setXXX,必须得在接口中设置该方法为@Adaptive,还要在setXXX方法中添加URL参数,否则生成的Adaptive类中的setXXX方法会抛出异常。因此不能进行注入了。
8. SPI总结
- 两个核心的注解:@Adaptive,@SPI. 得弄清楚两者的含义:@SPI只是指明该接口支持SPI的方式实现;@Adaptive,用在实现类上时,Dubbo在获取接口的自适应类时,就不会再使用AdaptiveClassCodeGenerator去生成自适应类了,而是直接返回标注了@Adaptive的类。用在方法上时,自适应的类要根据方法的URL参数中在运行时,决定生成哪一个实例。
- getExtension(name): 直接根据name去生成实例,是确定的
- getAdaptiveExtension: 生成自适应类
网友评论