dubbo源码(章节二) -- dubbo的Ioc

2018-10-24 08:45:48来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

上一篇主要分析了extensionLoader的获取,以及获取extension的第一种方式,即通过装饰类或者动态代理的方式,今天我们首先从获取extension的第二种方式说起。

/**
 * Find the extension with the given name.
 */
getExtension(String name)

 

下面讨论getExtension(String name)

先从这个方法的代码跟踪开始,

1 public T getExtension(String name) {
2   Holder<Object> holder = cachedInstances.get(name); 3   ......
4   Object instance = holder.get(); 5   ......
6   instance = createExtension(name);
7   return (T) instance; 8 }

这里省略了比较多的内容,有了前面一篇跟踪代码的经验,缓存的具体使用就不再贴出来了,我们只看主要逻辑即可,跟进去看createExtension(name),

 1 private T createExtension(String name) {
 2   Class<?> clazz = getExtensionClasses().get(name); 
3
  T instance = clazz.newInstance(); 4
5   injectExtension(instance); 6   Set<Class<?>> wrapperClasses = cachedWrapperClasses;
7   for (Class<?> wrapperClass : wrapperClasses) { 8     instance =
9     injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
10   }
11   return instance;
12 }

这里先是通过getExtensionClasses().get(name)拿到一个class对象,getExtensionClasses()方法上一篇说过了,它最终所赋值的是type扩展的所有实现中,既没有@Adaptive注解,也不包含type类型的构造器(这一类扩展实现我们称之为包装类)的那些实现类,并且被缓存在cachedClasses map中,map的key即为实现类在dubbo spi配置文件中的类名,这里提一个细节,如果在cachedClasses中没有拿到key为name对应的value,这里就会抛出异常,那也就是说,getExtension(name)这种方式,只能用来获取既非@Adaptive注解,又非包装类的那些类的实现,因为只有这样的类才会被缓存在cachedClasses中。这里拿到这个类并实例化一个Instance,然后调用了方法injectExtension(instance),这个方法看名字就知道了,inject既就是注射的意思,这里就将实现dubbo的依赖注入,现在来看这个方法的实现,

 1 private T injectExtension(T instance) { 
2
  for (Method method : instance.getClass().getMethods()) { 3     if (method.getName().startsWith("set") 4         && method.getParameterTypes().length == 1 5         && Modifier.isPublic(method.getModifiers())) { 6       Class<?> pt = method.getParameterTypes()[0]; 7
8       String property = method.getName().length() > 3 ?
9         method.getName().substring(3, 4).toLowerCase() +
10         method.getName().substring(4) : ""; 11       Object object = objectFactory.getExtension(pt, property);
12       method.invoke(instance, object);
13     }
14   }
15   return instance; 16 }

把这个class的所有方法都提取出来,然后做了一系列的判断,这就是要通过setter方法为对象注入属性了。被注入的object是通过objectFactory.getExtension(...)得到的,回忆一下上一篇说过,每个extension对象都会有一个objectFactory,objectFactory就是一个AdaptiveExtensionFactory,它的作用是:为dubbo的Ioc提供所有对象。所以这里我们看到,setter方法要注入的属性值,是通过这个扩展的objectFactory拿到的,我们跟进去看一下它的实现,

1 public <T> T getExtension(Class<T> type, String name) {
2   for (ExtensionFactory factory : factories) {
3     T extension = factory.getExtension(type, name);
4     return extension;
5   }
6 }

这里会遍历factories,每个元素都是ExtensionFactory的一个非Adaptive实现,我们在上一篇已经看到了,ExtensionFactory的非Adpative实现,最终被放入factories中的,是SpiExtensionFactory,不过实际上在另外的包里还有一个ExtensionFactory的非Adaptive实现类:SpringExtensionFactory,这里我们不妨把SpiExtensionFactory和SpringExtensionFactory同时拿来分析,可以看到只要这里getExtension(type,name)返回非空,就直接返回所获取的这个值,我们依次看下这两个实现分别是怎么做的,先看SpiExtensionFactory:

1 public <T> T getExtension(Class<T> type, String name) {
2   if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
3     ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
4     if (!loader.getSupportedExtensions().isEmpty()) {
5       return loader.getAdaptiveExtension();
6     }
7   }
8   return null;
9 }

这里判断type是不是@SPI标注的注解,如果是,说明这个type是一个dubbo spi扩展,那么就返回它的一个AdaptiveExtension,它为什么叫SpiExtensionFactory呢?就是获取Spi扩展的一个factory,获取spi扩展的adaptive实现的做法在上一篇里已经讨论过了。也就是说,如果一个扩展实现类中需要注入另一个spi扩展的实现,那就是通过它的objectFactory里的SpiExtensionFactory来获取需要注入的这个扩展的Adaptive实现。

那么如果要注入的对象不是dubbo spi扩展呢,这里不会进入if,就会返回null,我们接下来看SpringExtensionFactory,

 1 public <T> T getExtension(Class<T> type, String name) {
 2   for (ApplicationContext context : contexts) {
 3     if (context.containsBean(name)) {
 4       Object bean = context.getBean(name); 
5
      return (T) bean;
6
    } 7   } 8 9   for (ApplicationContext context : contexts) {
10     return context.getBean(type);
11   } 12   return null; 13 }

SpringExtensionFactory就很简单了,直接从spring的ApplicationContext中尝试获取bean,先尝试通过name获取,如果by name失败,再尝试通过by type来获取。也就是说,如果一个扩展实现类中需要注入一个普通对象(非Spi注解的dubbo扩展),那就通过它的objectFactory里的SpringExtensionFactory来获取这个要被注入的类对象,当然前提是spring容器中已经注入了这个对象。

ok,通过objectFactory提供的对象,我们完成了extension属性的注入,不过createExtension方法所做的并不止这些,我们回到injectExtension的调用处,即createExtension方法的第五行,接着往下看,代码第六行获取了缓存cachedWrapperClasses,上一篇讲了,这个变量缓存了所有非@Adaptive注解同时包含了带有本扩展类型的构造器方法的那些扩展实现类。实际上从这个缓存的名字里就能看出来,Wrapper意指包装,就是说这里所包含的类都是包装类。为了解释这行代码,这里必须举一个例子:我们看接口Protocol,

@SPI("dubbo")
public interface Protocol{
  ......  
}

这是一个@Spi注解的dubbo扩展接口,考虑它的三个实现,MockProtocol,ProtocolFilterWrapper,ProtocolListenerWrapper,

final public class MockProtocol implements Protocol {
  @Override
  public int getDefaultPort() {
    return 0;
  }
  ......
}
public class ProtocolFilterWrapper implements Protocol {
  private final Protocol protocol;

  public ProtocolFilterWrapper(Protocol protocol) {
    if (protocol == null) {
      throw new IllegalArgumentException(......);
    }
    this.protocol = protocol;
  }
  @Override
  public int getDefaultPort() {
    return protocol.getDefaultPort();
  }
  ...... }
public class ProtocolListenerWrapper implements Protocol {
  private final Protocol protocol;

  public ProtocolListenerWrapper(Protocol protocol) {
    if (protocol == null) {
      throw new IllegalArgumentException(......);
    }
    this.protocol = protocol;
  }
  @Override
  public int getDefaultPort() {
    return protocol.getDefaultPort();
  }
  ...... }

可以看到,这三个类都没有被@Adaptive注解,其中ProtocolFilterWrapper,ProtocolListenerWrapper都有一个私有属性Protocol,同时有一个Protocol类型作为入参的构造器,所以在类加载之后,这两个类都会被放入cachedWrapperClasses缓存中,而MockProtocol则既不被@Adaptive注解,也不包含Protocol作为入参的构造器,它在类加载之后会被放入cachedClasses中,所以它是可以被通过第二种获取扩展对象的方式获取的:

ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("mock");

其中"mock"为dubbo spi配置文件中该类的name。

ok,当MockProtocol被注入了属性之后,代码获取了cachedWrapperClasses的值,然后依次取出其中缓存的类,初始化它们,并将当前protocol作为参数传入构造器,同时将返回的protocol赋值给当前instance。所以,如果cachedWrapperClasses中的顺序是:ProtocolFilterWrapper,ProtocolListenerWrapper,那么执行完上述代码之后,在createExtension方法的最后一行,我们最终获取到的instance将不再是MockProtocol的实例,而是ProtocolListenerWrapper的实例,它里面拥有一个protocol属性,此protocol将会是ProtocolFilterWrapper的实例,而它里面还是拥有一个protocol属性,这个protocol才是我们一开始就拿到的MockProtocol的实例。如下所示;

instance(ProtocolListenerWrapper@947)
    -->protocol(ProtocolFilterWrapper@950)
        -->protocol(MockProtocol@874)

看到这,想必大家就明白了为什么这一类的实现类,会被称为包装类了吧。如果调用instance的方法,例如代码中贴出来的getDefaultPort(),就将从包装的最外层开始向内调用。

ok,这一篇我们分析了dubbo获取扩展实现的第二种方式,同时分析了dubbo Ioc的原理,总结下dubbo的spi:

  • 要获取dubbo spi接口的实现,就要先获取对应的ExtensionLoader,而通过loader获取实现的方式有两种。
  • getExtensionClasses()方法会加载配置文件中配置的该接口的所有实现,并赋值给相应的缓存:
    • 接口的所有实现中,要么存在唯一的一个类被@Adaptive注解,要么就动态生成一个Adaptive代理类。这个类被缓存在cachedAdaptiveClass中,我们称之为第一种实现类。
    • 接口的所有实现中,如果存在一些实现,没有被@Adaptive注解,但是包含一个以该接口类型为参数的构造器,称这种类为第二种实现类或包装类,它们被缓存在cachedWrapperClasses中。
    • 剩下的实现类,既不被@Adaptive注解,也不包含特殊的构造器,我们称之为第三种实现类,它们被缓存在cachedClasses中。
  • getAdaptiveExtension()方法将获得扩展接口的装饰模式的实现类,这个类有且只有一个。
  • getExtension(name)方法根据配置文件中的类的name来获取扩展实现类,只有第三种实现类能通过这种方式被获取,但是如果该接口有包装类存在,那么此方法获取的永远是被包装的类。

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:简单来看看JavaBean

下一篇:Java并发编程之原子操作解读