摘要:上一篇文章,我详细的介绍了Java SPI和Dubbo SPI以及自适应扩展机制有关知识点,并辅以演示案例,本文也是基础上一篇文章接过来的。有兴趣的可以看看!接下来着重进行源码分析。
Dubbo SPI及自适应扩展机制介绍篇:https://blog.youkuaiyun.com/lkp_kapila/article/details/105676828
一.静态扩展机制
1.下面为静态扩展机制的实现方式,通过传入固定的key来生成对应的实例,缺点时不够灵活。
//通过指定扩展名获取对应的实例 MakeCar makeCar = ExtensionLoader.getExtensionLoader(MakeCar.class).getExtension("car");
2.进入源码,让我们先看看getExtensionLoader方法做了哪些事情,一步步的进行分析。
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { //当前类型是否为接口 if (!type.isInterface()) { throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!"); } //当前类型是否带有@SPI注解 if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!"); } 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; } private static <T> boolean withExtensionAnnotation(Class<T> type) { //当前类型是否带有@SPI注解 return type.isAnnotationPresent(SPI.class); }
此处的逻辑很清晰,首先判断当前传入的class类型是否为接口且带有@SPI注解,然后从缓存获取ExtensionLoader对象。
看看是怎么根据type来创建的。
private ExtensionLoader(Class<?> type) { this.type = type; //type不为ExtensionFactory,则根据ExtensionFactory来获取自适应扩展点 objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
大致的意思是type不为ExtensionFactory,则根据ExtensionFactory来获取自适应扩展点,继续往下看,代码进入getAdaptiveExtension方法中,此时已经创建了ExtensionLoader对象,是基于ExtensionFactory创建的,开始获取自适应扩展机制返回的实例。
public T getAdaptiveExtension() { //优先从缓存的Adaptive中获取 Object instance = cachedAdaptiveInstance.get(); if (instance == null) { if (createAdaptiveInstanceError == null) { synchronized (cachedAdaptiveInstance) { instance = cachedAdaptiveInstance.get(); if (instance == null) { //缓存没有,则开始创建 instance = createAdaptiveExtension(); //创建完成后,加入缓存,便于后续的重复创建造成性能浪费 cachedAdaptiveInstance.set(instance); } } } else { throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } } return (T) instance; }
这段代码就是先从缓存拿,有则直接返回,没有则进行创建。继续看创建的代码。
private T createAdaptiveExtension() { try { /** *1.先获取到自适应扩展点对应的class对象 *2.根据class对象反射生成实例 *3.完成属性的setter注入 */ return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e); } }
这段代码比较重要,注释我介绍的很清楚了,现在让我们一步一步的分析怎么实现的。
private Class<?> getAdaptiveExtensionClass() { //获取可扩展的类信息,并加入到缓存中 getExtensionClasses(); if (cachedAdaptiveClass != null) { //缓存中有则直接返回 return cachedAdaptiveClass; } //缓存没有,则进行自动生成 return cachedAdaptiveClass = createAdaptiveExtensionClass(); } 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; }
开始加载可扩展的Class对象信息,也是先从缓存获取,有则直接返回,没有则开始加载,加载完成后存放在缓存中。继续看下是如何进行加载的。
private Map<String, Class<?>> loadExtensionClasses() { //获取默认的扩展实现类 cacheDefaultExtensionName(); //开始扫描/META-INF/dubbo等路径下文件,以类全限定名为key Map<String, Class<?>> extensionClasses = new HashMap<>(); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName()); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); //扫描完成,加入到想应缓存中 return extensionClasses; } private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { if (clazz.isAnnotationPresent(Adaptive.class)) { //缓存标注了@Adaptive注解的类信息 cacheAdaptiveClass(clazz); } else if (isWrapperClass(clazz)) { //缓存包装类,进行对象的一个增强,在Prcotol中是进行了增强的 cacheWrapperClass(clazz); } else { clazz.getConstructor(); if (StringUtils.isEmpty(name)) { 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 (ArrayUtils.isNotEmpty(names)) { cacheActivateClass(clazz, names[0]); for (String n : names) { cacheName(clazz, n); saveInExtensionClass(extensionClasses, clazz, name); } } } } private void cacheAdaptiveClass(Class<?> clazz) { if (cachedAdaptiveClass == null) { //将当前的class赋值给缓存 cachedAdaptiveClass = clazz; } else if (!cachedAdaptiveClass.equals(clazz)) { throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName()); } } private void cacheWrapperClass(Class<?> clazz) { if (cachedWrapperClasses == null) { cachedWrapperClasses = new ConcurrentHashSet<>(); } //加入到缓存中 cachedWrapperClasses.add(clazz); }
在全局扫描时,对org.apache.dubbo.common.extension.ExtensionFactory文件进行一个解析,并进行一个相应的处理。
adaptive=org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory spring=org.apache.dubbo.config.spring.extension.SpringExtensionFactory @Adaptive public class AdaptiveExtensionFactory implements ExtensionFactory {
因为AdaptiveExtensionFactory类标注了@Adaptive注解,所有会将其缓存到adaptiveClass缓存中。加载完成之后,通过反射创建实例,调用的AdaptiveExtensionFactory构造方法。
public AdaptiveExtensionFactory() { //获取ExtensionLoader ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class); List<ExtensionFactory> list = new ArrayList<ExtensionFactory>(); //获取extensionClass for (String name : loader.getSupportedExtensions()) { //将对应的实例加入集合中 list.add(loader.getExtension(name)); } //初始化工厂 factories = Collections.unmodifiableList(list); }
可以看到,此时extesionFactory加载的是SpiExtensionFatory以及SpringExtensionFatory用来实例化ExtensionLoader对象的objectFatory。此处初始化对象工厂的目的是为了自动注入时通过这两种加载机制去获取实例完成setter注入,Spring的扩展工厂则时通过getBean方式去获取的。创建完后开始进行属性的setter注入。此时就先暂时不延伸了,后续涉及到注入再详细解析。此时返回的是一个AdaptiveExtensionFactory的class对象,此处就完成了工厂的初始化工作了。现在开始进入第二步,getExtension方法,这里是获取具体实例的实现。
public T getExtension(String name) { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("Extension name == null"); } if ("true".equals(name)) { //如果此处传入了true,则返回默认的扩展点 return getDefaultExtension(); } //从缓存中获取,有则直接返回 Holder<Object> holder = getOrCreateHolder(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; }
此段代码逻辑比较清晰,一看便知。下面我们看下创建过程。
private T createExtension(String name) { //从已加载的类信息缓存中获取,没有会动态加载,扫描获取 Class<?> clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } //先缓存实例中获取 T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { //没有则反射创建,并加入缓存 EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } //进行属性注入,setter方式注入 injectExtension(instance); //获取缓存的保证类信息,完成对目标对象的包装 Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (CollectionUtils.isNotEmpty(wrapperClasses)) { for (Class<?> wrapperClass : wrapperClasses) { //创建实例进行setter注入 instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; }
此处的逻辑很清晰,首先从缓存中获取是否已经有加载过类,有的话根据key获取到对应的value。而类的扫描前面已经讲过,这里不再赘述。然后通过缓存实例获取是否存在,不存在反射创建并加入。然后进行setter注入,有属性则注入没属性则跳过。然后判断是否存在需要包装的类,前面已经讲解过包装类信息在何时加载,这里不再赘述。有的话会进行层层包装,目的是为了增强目标对象。然后将对象返回。到这里,通过静态扩展机制获取目标对象就结束了。可以看到,Dubbo SPI中使用了大量的缓存对象,这也是性能提升一种方式。
二.默认扩展机制
默认扩展机制,顾名思义是通过提前设置一个默认值,在加载时会自动进行匹配。可通过在@SPI上标明属性value值,如@SPI("car"),那么在获取时,会自动去创建car对应的实例,下面开始讲解源码。
public static void main(String[] args) { //静态扩展时传入true MakeCar makeCar1 = ExtensionLoader.getExtensionLoader(MakeCar.class).getExtension("true"); //获取默认扩展 MakeCar makeCar2 = ExtensionLoader.getExtensionLoader(MakeCar.class).getDefaultExtension(); }
以上的这两种方式在实现上都一样,分别解释下。
public T getExtension(String name) { //name为true if ("true".equals(name)) { return getDefaultExtension(); } }
可以看到,通过传入true最后的逻辑也时获取默认扩展,让我们点进去看下怎么获取的。
public T getDefaultExtension() { //获取加载类信息,此处会赋值默认的name getExtensionClasses(); //判断默认name if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) { return null; } return getExtension(cachedDefaultName); } private Map<String, Class<?>> loadExtensionClasses() { cacheDefaultExtensionName(); } //解析默认配置 private void cacheDefaultExtensionName() { //拿到SPI注解 final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation != null) { //获取value值 String value = defaultAnnotation.value(); if ((value = value.trim()).length() > 0) { String[] names = NAME_SEPARATOR.split(value); //如果解析到value数组大于1,则报错,因为默认只有一个 if (names.length > 1) { throw new IllegalStateException("More than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names)); } if (names.length == 1) { //将数组第一个元素赋值给默认的name cachedDefaultName = names[0]; } } } }
这里重点要看的cacheDefaultExtensionName方法,此方法的目的时拿到@SPI注解上的value,然后赋值默认的name。如果配置了name那么走静态扩展机制那套逻辑然后返回对应的实例,如果没配置则返回null,逻辑比较简单。
三.自适应扩展机制(实现类上标注@Adaptive注解)
在获取自适应扩展点时,如果实现类上标注了@Adaptive,那么会优先创建此实例。在讲解静态扩展机制时不知道还有没有印象。在创建ExtensionFatory工厂时,是不是最终返回了一个AbstractExtensionFatory实例,那是因为此类标注了@Adaptive注解,下面进入源码分析。
MakeCar makeCar1 = ExtensionLoader.getExtensionLoader(MakeCar.class).getAdaptiveExtension();
可能大家会有印象,在获取ExtensionFatory自适应扩展机制时,首先会进行类信息的扫描,扫描到之后会有这么一步操作。将带有@Adaptive注解的类加入到adaptiveClass缓存中,如果有则直接将其返回进行反射创建实例。
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { if (clazz.isAnnotationPresent(Adaptive.class)) { //缓存标注了@Adaptive注解的类信息 cacheAdaptiveClass(clazz); } //省略无关代码... } private void cacheAdaptiveClass(Class<?> clazz) { if (cachedAdaptiveClass == null) { //将当前的class赋值给缓存 cachedAdaptiveClass = clazz; } else if (!cachedAdaptiveClass.equals(clazz)) { throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName()); } }
逻辑非常清晰,最终通过getAdaptiveClass获取到后,反射创建了实例。
四.自适应扩展机制(接口方法上标注@Adaptive)
此种机制在Dubbo中应用时最为广泛的,同时也时最为复杂的。它是通过动态创建接口的实现类,类名为Xxx$Adaptive,然后实现接口中带有@Adaptive注解的方法,下面让我们来看看其具体实现方式。
private T createAdaptiveExtension() { try { /** *1.先获取到自适应扩展点对应的class对象 *2.根据class对象反射生成实例 *3.完成属性的setter注入 */ return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e); } } private Class<?> getAdaptiveExtensionClass() { //获取可扩展的类信息,并加入到缓存中 getExtensionClasses(); if (cachedAdaptiveClass != null) { //缓存中有则直接返回 return cachedAdaptiveClass; } //缓存没有,则进行自动生成 return cachedAdaptiveClass = createAdaptiveExtensionClass(); }
因为此时我们并没有在实现类中标注@Adaptive,所有缓存为空,进入创建的方法,此处开始自动生成。
private Class<?> createAdaptiveExtensionClass() { //动态拼接生成代码类如Xxx@Adaptive类 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); }
代码逻辑很清晰,动态生产自适应类,此处会判断方法是否带有@Adaptive注解,并且判断方法的参数是否有URL或者存在URL属性,因为动态创建的类会进行url参数的获取,此时若没有,Dubbo会抛出异常。
Failed to create adaptive class for interface com.gupao.vip.spi.Robot: not found url parameter or url attribute in parameters of method sayHello
此时通过反射创建实例,并完成setter注入,完成了自适应扩展点对应实例的创建。
五.Dubbo SPI中的Setter注入
在前面的集中扩展机制中,多多少少涉及到了依赖注入,即在某一个实现类中,带有其他接口的属性,而此属性也属于扩展机制中的一部分。那么我们可以为其生成set方法,在获取对应实现类实例时对其进行set注入。下面我们直接进入依赖注入的代码,一探究竟。
private T injectExtension(T instance) { //objectFatory对象即为extensionLoader中的属性,在最初创建ExtentsionFactory时已完成初始化 if (objectFactory != null) { //遍历所有方法 for (Method method : instance.getClass().getMethods()) { //是否为set方法,这是注入的前提 if (isSetter(method)) { //如果该方法带有禁止注入的注解,直接跳过 if (method.getAnnotation(DisableInject.class) != null) { continue; } Class<?> pt = method.getParameterTypes()[0]; if (ReflectUtils.isPrimitives(pt)) { continue; } String property = getSetterProperty(method); //此处调用ExtensionFatory对应实例的getExtension去获取实例 Object object = objectFactory.getExtension(pt, property); if (object != null) { //完成属性的赋值 method.invoke(instance, object); } } } } return instance; }
大致梳理一下流程,首先objectFatory对象不能为空,为空的话我们无法获取到需要注入的对象。再回顾一下此对象是如何而来的。
private ExtensionLoader(Class<?> type) { this.type = type; //此处返回的AbstractExtensionFatory objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
其次方法必须为set方法,其他方法直接跳过。接下来进行静态扩展,此处调用的是AbstractExtensionFatory类中的getExtension方法,让我们进去看看。
public <T> T getExtension(Class<T> type, String name) { //遍历工厂 for (ExtensionFactory factory : factories) { //分别调用对应的工厂去获取实例 T extension = factory.getExtension(type, name); if (extension != null) { return extension; } } return null; }
此处的factoies中保存着SpiExtensionFatory对象以及SpringExtensionFatory对象,遍历后分别调用对应的getExtension方法来获取实例。
//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()) { return loader.getAdaptiveExtension(); } } return null; } //SpringExtensionFactory public <T> T getExtension(Class<T> type, String name) { //为接口类型且标注了SPI if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { return null; } for (ApplicationContext context : CONTEXTS) { if (context.containsBean(name)) { //从IOC容器去去获取 Object bean = context.getBean(name); if (type.isInstance(bean)) { return (T) bean; } } } return null; }
如果是SPI则获取动态扩展点,如果是Spring则通过从IOC容器获取的方式进行依赖注入。
六.激活扩展机制
也就是ActiveExtension。通过group可区分,这里就不再详细介绍了,有兴趣的可以查资料进行了解。
总结:以上就是DubboSPI的几种扩展机制的原理,其中自适应扩展机制应用最广泛,对应想了解Dubbo服务发布,服务注册,服务消费等原理的同学,这块必须得弄明白了,这算是一个前提,不然会比较绕的。先介绍到这了,谢谢大家!