返回顶部
首页 > 资讯 > 后端开发 > Python >解析Apache Dubbo的SPI实现机制
  • 415
分享到

解析Apache Dubbo的SPI实现机制

2024-04-02 19:04:59 415人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

目录一、SPI1.1、jdk自带SPI实现1.2、dubbo SPI二、加载-ExtensionLoader2.1、获取ExtensionLoader的实例2.2、加载扩展类2.2.

一、SPI

在Java中,SPI体现了面向接口编程的思想,满足开闭设计原则。

1.1、JDK自带SPI实现

从JDK1.6开始引入SPI机制后,可以看到很多使用SPI的案例,比如最常见的数据库驱动实现,在JDK中只定义了java.sql.Driver的接口,具体实现由各数据库厂商来提供。下面一个简单的例子来快速了解下Java SPI的使用方式:

1)定义一个接口


package com.vivo.study
public interface Car {
void getPrice();
}

2)接口实现


package com.vivo.study.impl
 

public class AudiCar implements Car {
    @Override
    public void getPrice() {
        System.out.println("Audi A6L's price is  500000 RMB.");
    }
}
 
package com.vivo.study.impl

public class BydCar implements Car {
    @Override
    public void getPrice() {
        System.out.println("BYD han's price is 220000 RMB.");
    }
}

3)挂载扩展类信息

在META-INF/services目录下以接口全名为文件名的文本文件,对应此处即在META-INF/services目录下创建一个文件名为com.vivo.study.Car的文件,文件内容如下:


com.vivo.study.impl.AudiCar
com.vivo.study.impl.BydCar

4)使用


public class SpiDemo {
public static void main(String[] args) {
        ServiceLoader<Car> load = ServiceLoader.load(Car.class);
        Iterator<Car> iterator = load.iterator();
while (iterator.hasNext()) {
            Car car = iterator.next();
            car.getPrice();
        }
    }
}

上面的例子简单的介绍了JDK SPI机制的使用方式,其中最关键的类为ServiceLoader,通过ServiceLoader类来加载接口的实现类,ServiceLoader是Iterable接口的实现类,对于ServiceLoader加载的详细过程此处不展开。

JDK对SPI的加载实现存在一个较为突出的小缺点,无法按需加载实现类,通过ServiceLoader.load加载时会将文件中的所有实现都进行实例化,如果想要获取具体某个具体的实现类需要进行遍历判断。

1.2、Dubbo SPI

SPI扩展是Dubbo的最大的优点之一,支持协议扩展、调用拦截扩展、引用监听扩展等等。在Dubbo中,根据不同的扩展定位,扩展文件分别被放置在META-INF/dubbo/internal/,META-INF/dubbo/,META-INF/services/这三个路径下。

Dubbo中有直接使用JDK SPI实现的方式,比如org.apache.dubbo.common.extension.LoadingStrategy放在META-INF/services/路径下,但大多情况下都是使用其自身对JDK SPI的实现的一种优化方式,可称为Dubbo SPI,也就是本文要讲解的点。

相比于JDK的SPI的实现,Dubbo SPI具有以下特点:

  • 配置形式更灵活:支持以key:value的形式在文件里配置类似name:xxx.xxx.xxx.xx,后续可以通过name来进行扩展类按需精准获取。
  • 缓存的使用:使用缓存提升性能,保证一个扩展实现类至多会加载一次。
  • 对扩展类细分扩展:支持扩展点自动包装(Wrapper)、扩展点自动装配、扩展点自适应(@Adaptive)、扩展点自动激活(@Activate)。

Dubbo对扩展点的加载主要由ExtensionLoader这个类展开。

二、加载-ExtensionLoader

ExtensionLoader在Dubbo里的角色类似ServiceLoader在JDK中的存在,用于加载扩展类。在Dubbo源码里,随处都可以见到ExtensionLoader的身影,比如在服务暴露里的关键类ServiceConfig中等,弄清楚ExtensionLoader的实现细节对阅读Dubbo源码有很大的帮助。

2.1、获取ExtensionLoader的实例

ExtensionLoader没有提供共有的构造函数,

只能通过ExtensionLoader.getExtensionLoader(Class type)来获取ExtensionLoader实例。


public
 
    // ConcurrentHashMap缓存,key -> Class value -> ExtensionLoader实例
    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
 
    private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }
 
    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
        }
        // 检查是否是接口,如果不是则抛出异常
        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() + "!");
        }
 
        // 从缓存里取,没有则初始化一个并放入缓存EXTENSION_LOADERS中
        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实例的过程,可以看出,每一个被@SPI修饰的接口都会对应同一个ExtensionLoader实例,且对应ExtensionLoader只会被初始化一次,并缓存在ConcurresntHashMap中。

2.2、加载扩展类

加载扩展类入口,当使用ExtensionLoader时,getExtensionName、getActivateExtension或是getDefaultExtension都要经过getExtensionClasses方法来加载扩展类,如下图;

getExtensionClasses方法调用的路径如下图,getExtensionClasses是加载扩展类的一个起点,会首先从缓存中获取,如果缓存中没有则通过loadExtensionClasses方法来加载扩展类,所以说实际上的加载逻辑入口在loadExtensionClasses。

getExtensionClasses

  |->loadExtensionClasses

    |->cacheDefaultExtensionName

    |->loadDirectory

      |->loadResource

        |->loadClass

2.2.1、loadExtensionClasses加载扩展类

由于整个加载过程设计的源码较多,因此用一个流程图来进行描述,具体细节可以结合源码进行查看。

loadExtensionClasses主要做了以下这几件事:

默认扩展名:

抽取默认扩展实现名并缓存在ExtensionLoader里的cachedDefaultName,默认扩展名配置通过@SPI注解在接口上配置,如配置@SPI("defaultName")则默认扩展名为defaultName。

加载扩展类信息:

从META-INF/dubbo/internal/,META-INF/dubbo/,META-INF/services/这三个路径下寻找以类的全路径名命名的文件,并逐行读取文件里的内容。

加载class并缓存:

对扩展类分为自适应扩展实现类(被@Adaptive修饰的实现类)、包装类(拥有一个只有一个为这个接口类型的参数的构造方法)、普通扩展类,其中普通扩展类中又包含自动激活扩展类(被@Activate修饰的类)和真普通的类,对自适应扩展实现类、包装类、自动激活扩展类这三种类型的类分别加载并分别缓存到cachedAdaptiveClass、cachedActivates、cachedWrapperClasses。

返回结果Map<String, Class<?>>:

结果返回Map,其中key对应扩展文件里配置的name,value对应扩展的类class,最后在getExtensionClasses方法里会将此结果放入缓存cachedClasses中。此结果Map中包含除了自适应扩展实现类和包装实现类的其他所用的扩展类名与对应类的映射关系。

通过loadExtensionClasses方法把扩展类(Class对象)都加载到相应的缓存中,是为了方便后面实例化扩展类对象,通过newInstance()等方法来实例化。

2.2.2、扩展包装类

什么是扩展包装类?是不是类名结尾包含了Wrapper的类就是扩展包装类?

在Dubbo SPI接口的实现扩展类中,如果此类包含一个此接口作为参数的构造方法,则为扩展包装类。扩展包装类的作用是持有具体的扩展实现类,可以一层一层的被包装,作用类似aop

包装扩展类的作用是类似AOP,方便扩展增强。具体实现代码如下:

从代码中可以得出,可以通过boolean wrap选择是否使用包装类,默认情况下为true;如果有扩展包装类,实际的实现类会被包装类按一定的顺序一层一层包起来。

如Protocol的实现类ProtocolFilterWrapper、ProtocolListenerWrapper都是扩展包装类。

2.2.3、自适应扩展实现类

@Adaptive


@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
String[] value() default {};
}

从源码以及源码注释中可以得出以下几点:

  • Adaptive是一个注解,可以修饰类(接口,枚举)和方法。
  • 此注解的作用是为ExtensionLoader注入扩展实例提供有用的信息。

从注释中理解value的作用:

  • value可以决定选择使用具体的扩展类。
  • 通过value配置的值key,在修饰的方法的入参org.apache.dubbo.common.URL中通过key获取到对应的值value,根据value作为extensionName去决定使用对应的扩展类。
  • 如果通过2没有找到对应的扩展,会选择默认的扩展类,通过@SPI配置默认扩展类。

@Adaptive简单例子

由于@Adaptive修饰类时比较好理解,这里举一个@Adaptive修饰方法的例子,使用@Adaptive修饰方法的这种情况在Dubbo也是随处可见。



@SPI("impl1")
public interface SimpleExt {
    @Adaptive({"key1", "key2"})
    String yell(URL url, String s);
}

如果调用

ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension().yell(url, s)方法,最终调用哪一个扩展类的实例去执行yell方法的流程大致为:先获取扩展类的名称extName(对应上面说的name:class中的name),然后通过extName来获取对应的类Class,再实例化进行调用。所以关键的步骤在怎么得到extName,上面的这个例子得到extName的流程为:

  • 通过url.getParameters.get("key1")获取,
  • 没有获取到则用url.getParameters.get("key2"),如果还是没有获取到则使用impl1对应的实现类,最后还是没有获取到则抛异常IllegalStateException。

可以看出,@Adaptive的好处就是可以通过方法入参决定具体调用哪一个实现类。下面会对@Adaptive的具体实现进行详细分析。

@Adaptive加载流程

流程关键点说明:

1)黄色标记的,cachedAdaptiveClass是在ExtensionLoader#loadClass方法中加载Extension类时缓存的。

2)绿色标记的,如果Extension类中存在被@Adaptive修饰的类时会使用该类来初始化实例。

3)红色标记的,如果Extension类中不存在被@Adaptive修饰的类时,则需要动态生成代码,通过javassist(默认)来编译生成Xxxx$Adaptive类来实例化。

4)实例化后通过injectExtension来将Adaptive实例的Extension注入(属性注入)。

后续围绕上述的关键点3详细展开,关键点4此处不展开。

动态生成Xxx$Adaptive类:下面的代码为动态生成Adaptive类的相关代码,具体生成代码的细节在AdaptiveClassCodeGenerator#generate中


public class ExtensionLoader<T> {
// ...
private Class<?> getAdaptiveExtensionClass() {
// 根据对应的SPI文件加载扩展类并缓存,细节此处不展开
        getExtensionClasses();
// 如果存在被@Adaptive修饰的类则直接返回此类
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
        }
// 动态生成Xxxx$Adaptive类
return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }
private Class<?> createAdaptiveExtensionClass() {
// 生成Xxxx$Adaptive类代码,可自行加日志或断点查看生成的代码
        String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
        ClassLoader classLoader = findClassLoader();
// 获取动态编译器,默认为javassist
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
    }
}

AdaptiveClassCodeGenerator#generate生成code的方式是通过字符串拼接,大量使用String.fORMat,整个代码过程比较繁琐,可通过debug去了解细节。

最关键的的部分是生成被@Adaptive修饰的方法的内容,也就是最终调用实例的@Adaptive方法时,可通过参数来动态选择具体使用哪个扩展实例。下面对此部分进行分析:


public class AdaptiveClassCodeGenerator {
// ...

private String generateMethodContent(Method method) {
// 获取方法上的@Adaptive注解
        Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
        StringBuilder code = new StringBuilder(512);
if (adaptiveAnnotation == null) {
// 方法时没有@Adaptive注解,生成不支持的代码
return generateUnsupported(method);
        } else {
// 方法参数里URL是第几个参数,不存在则为-1
int urlTypeIndex = getUrlTypeIndex(method);
// found parameter in URL type
if (urlTypeIndex != -1) {
// Null Point check
                code.append(generateUrlNullCheck(urlTypeIndex));
            } else {
// did not find parameter in URL type
                code.append(generateUrlAssignmentIndirectly(method));
            }
// 获取方法上@Adaptive配置的value
// 比如 @Adaptive({"key1","key2"}),则会返回String数组{"key1","key2"}
// 如果@Adaptive没有配置value,则会根据简写接口名按驼峰用.分割,比如SimpleExt对应simple.ext
            String[] value = getMethodAdaptiveValue(adaptiveAnnotation);
// 参数里是否存在org.apache.dubbo.rpc.Invocation
            boolean hasInvocation = hasInvocationArgument(method);
            code.append(generateInvocationArgumentNullCheck(method));
// 生成String extName = xxx;的代码 ,extName用于获取具体的Extension实例
            code.append(generateExtNameAssignment(value, hasInvocation));
// check extName == null?
            code.append(generateExtNameNullCheck(value));
            code.append(generateExtensionAssignment());
// return statement
            code.append(generateReturnAndInvocation(method));
        }
return code.toString();
    }
}

上述生成Adaptive类的方法内容中最关键的步骤在生成extName的部分,也就是generateExtNameAssignment(value,hasInvocation),此方法if太多了(有点眼花缭乱)。

以下列举几个例子对此方法的实现流程进行简单展示:假设方法中的参数不包含org.apache.dubbo.rpc.Invocation,包含org.apache.dubbo.rpc.Invocation的情况会更加复杂。

1)方法被@Adaptive修饰,没有配置value,且在接口@SPI上配置了默认的实现


@SPI("impl1")
public interface SimpleExt {
@Adaptive
String echo(URL url, String s);
}

对应生成extName的代码为:


String extName = url.getParameter("simple.ext", "impl1")

2)方法被@Adaptive修饰,没有配置value,且在接口@SPI上没有配置默认的实现


@SPI("impl1")
public interface SimpleExt {
@Adaptive({"key1", "key2"})
String yell(URL url, String s);
}

对应生成extName的代码为:


String extName = url.getParameter( "simple.ext")

3)方法被@Adaptive修饰,配置了value(假设两个,依次类推),且在接口@SPI上配置了默认的实现


@SPI
public interface SimpleExt {
@Adaptive({"key1", "key2"})
String yell(URL url, String s);
}

对应生成extName的代码为:


String extName = url.getParameter("key1", url.getParameter("key2", "impl1"));

4)方法被@Adaptive修饰,配置了value(假设两个,依次类推),且在接口@SPI没有配置默认的实现


@SPI
public interface SimpleExt {
@Adaptive({"key1", "key2"})
String yell(URL url, String s);
}

对应生成extName的代码为:


String extName = url.getParameter("key1", url.getParameter("key2"));

完整的生成类可参见附录。

2.2.4、自动激活扩展类

如果你有扩展实现过Dubbo的Filter,那么一定会对@Activate很熟悉。@Activate注解的作用是可以通过给定的条件来自动激活扩展实现类,通过ExtensionLoader#getActivateExtension(URL,String, String)方法可以找到指定条件下需要激活的扩展类列表。

下面以一个例子来对@Activate的作用进行说明,在Consumer调用Dubbo接口时,会经过消费方的过滤器链以及提供方的过滤器链,在Provider暴露服务的过程中会拼接需要使用哪些Filter。

对应源码中的位置在ProtocolFilterWrapper#buildInvokerChain(invoker, key, group)方法中。


// export:key-> service.filter ; group-> provider
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    // 在Provider暴露服务服务export时,会根据获取Url中的service.filter对应的值和group=provider来获取激活对应的Filter
    List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
}

ExtensionLoader#getActivateExtension(URL, String, String)是怎么根据条件来自动激活对应的扩展类列表的可以自行查看该方法的代码,此处不展开。

三、总结

本文主要对Dubbo SPI机制的扩展类加载过程通过ExtensionLoader类源码来进行总结,可以概况为以下几点:

1.Dubbo SPI结合了JDK SPI的实现,并在此基础上进行优化,如精准按需加载扩展类、缓存提升性能。

2.分析ExtensionLoader加载扩展类的过程,加载META-INF/dubbo/internal/,META-INF/dubbo/,META-INF/services/这三个路径下的文件,并分类缓存在ExtensionLoader实例。

3.介绍扩展包装类及其实现过程,扩展包装类实现了类似AOP的功能。

4.自适应扩展类,分析@Adptive修饰方法时动态生成Xxx$Adaptive类的过程,以及通过参数自适应选择扩展实现类完成方法调用的案例介绍。

简单介绍自动激活扩展类及@Activate的作用。

四、附录

Xxx$Adaptive完整案例

@SPI接口定义


@SPI("impl1")
public interface SimpleExt {
    // @Adaptive example, do not specify a explicit key.
    @Adaptive
    String echo(URL url, String s);
 
    @Adaptive({"key1", "key2"})
    String yell(URL url, String s);
 
    // no @Adaptive
    String bang(URL url, int i);
}

生成的Adaptive类代码


package org.apache.dubbo.common.extension.ext1;
  
import org.apache.dubbo.common.extension.ExtensionLoader;
  
public class SimpleExt$Adaptive implements org.apache.dubbo.common.extension.ext1.SimpleExt {
  
    public java.lang.String yell(org.apache.dubbo.common.URL arg0, java.lang.String arg1) {
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg0;
        String extName = url.getParameter("key1", url.getParameter("key2", "impl1"));
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.apache.dubbo.common.extension.ext1.SimpleExt) name from url (" + url.toString() + ") use keys([key1, key2])");
        org.apache.dubbo.common.extension.ext1.SimpleExt extension = (org.apache.dubbo.common.extension.ext1.SimpleExt)ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.extension.ext1.SimpleExt.class).getExtension(extName);
        return extension.yell(arg0, arg1);
    }
  
    public java.lang.String echo(org.apache.dubbo.common.URL arg0, java.lang.String arg1) {
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg0;
        String extName = url.getParameter("simple.ext", "impl1");
        if (extName == null)
            throw new IllegalStateException("Failed to get extension (org.apache.dubbo.common.extension.ext1.SimpleExt) name from url (" + url.toString() + ") use keys([simple.ext])");
        org.apache.dubbo.common.extension.ext1.SimpleExt extension = (org.apache.dubbo.common.extension.ext1.SimpleExt) ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.extension.ext1.SimpleExt.class).getExtension(extName);
        return extension.echo(arg0, arg1);
    }
  
    public java.lang.String bang(org.apache.dubbo.common.URL arg0, int arg1) {
        throw new UnsupportedOperationException("The method public abstract java.lang.String org.apache.dubbo.common.extension.ext1.SimpleExt.bang(org.apache.dubbo.common.URL,int) of interface org.apache.dubbo.common.extension.ext1.SimpleExt is not adaptive method!");
    }
  
}

以上就是解析Apache Dubbo的SPI实现机制的详细内容,更多关于Apache Dubbo的SPI实现机制的资料请关注编程网其它相关文章!

--结束END--

本文标题: 解析Apache Dubbo的SPI实现机制

本文链接: https://lsjlt.com/news/128800.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 解析Apache Dubbo的SPI实现机制
    目录一、SPI1.1、JDK自带SPI实现1.2、Dubbo SPI二、加载-ExtensionLoader2.1、获取ExtensionLoader的实例2.2、加载扩展类2.2....
    99+
    2024-04-02
  • Apache Dubbo的SPI机制是如何实现的
    目录一、SPI1.1 JDK自带SPI实现 1.2 Dubbo SPI 二、加载-ExtensionLoader 2.1 获取ExtensionLoader的实例 2.2 加载扩展类...
    99+
    2024-04-02
  • Java和Dubbo的SPI机制原理解析
    SPI: 简单理解就是,你一个接口有多种实现,然后在代码运行时候,具体选用那个实现,这时候我们就可以通过一些特定的方式来告诉程序寻用那个实现类,这就是SPI。 JAVA的SPI 全称...
    99+
    2024-04-02
  • dubbo的spi机制有什么用途
    Dubbo的SPI(Service Provider Interface)机制可以实现插件化的扩展和灵活的配置,让用户可以在不修改源...
    99+
    2024-04-03
    dubbo
  • Java Spring Dubbo三种SPI机制的区别
    目录前言SPI 有什么用?​JDK SPI​Dubbo SPISpring SPI​对比​前言 SPI 全称为 Service Provider Interface,是一种服务发现机...
    99+
    2024-04-02
  • Dubbo扩展点SPI实践示例解析
    目录正文扩展点配置:扩展实现类:拦截配置文件:调用拦截扩展:拦截扩展说明:常用约定:实现细节:扩展点的几个特点:扩展点自动包装扩展点自动装配扩展点自适应扩展点自动激活正文 Dubbo...
    99+
    2022-11-13
    Dubbo扩展点SPI实践 Dubbo SPI扩展
  • SpringBoot的SPI机制源码解析
    目录一、从java类加载机制说起1.1 双亲委派模型1.2 双亲委派模型缺陷1.3 使用线程上下文类加载器(ContextClassLoader)加载1.4 使用类加载器加载资源文件...
    99+
    2022-12-21
    SpringBoot SPI机制 SpringBoot SPI
  • java框架基础之SPI机制实现及源码解析
    目录1 定义2 案例实现标准接口厂商的具体接口实现3 SPI机制源码分析3.1 load加载过程3.2 实例化过程1 定义 SPI 的全名为 Service Pr...
    99+
    2024-04-02
  • Java中怎么实现SPI机制
    Java中怎么实现SPI机制,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。2 什么是SPI机制SPI是Service Provider Interface 的简...
    99+
    2023-06-16
  • spring-boot中的SPI机制实例讲解
    一、从java类加载机制说起 java中的类加载器负载加载来自文件系统、网络或者其他来源的类文件。jvm的类加载器默认使用的是双亲委派模式。三种默认的类加载器Bootstrap Cl...
    99+
    2024-04-02
  • 怎么进行Java SPI机制的分析
    这篇文章将为大家详细讲解有关怎么进行Java SPI机制的分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。为什么需要SPI?思考一个场景,我们封装了一套服务,别人通过引入我们写好...
    99+
    2023-06-22
  • Dubbo源码解析之SPI(一):扩展类的加载过程
    Dubbo是一款开源的、高性能且轻量级的Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用、智能容错和负载均衡,以及服务自动注册和发现。Dubbo最早是阿里公司内部的RPC框架,于 2011 年开源,之后迅速成为国内该类开源...
    99+
    2023-06-05
  • 如何理解Java中的SPI机制
    本篇内容介绍了“如何理解Java中的SPI机制”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!SPI的概念SPI在Java中的全称为Servi...
    99+
    2023-06-15
  • java开发ServiceLoader实现机制及SPI应用
    目录前言如何绕过双亲委派模式ServiceLoader实现机制SPI在各个框架上的应用小结前言 做过java web开发的小伙伴大多数时候都需要链接数据库,这个时候就需要配置数据库引...
    99+
    2022-11-13
    java开发ServiceLoader SPI java ServiceLoader SPI
  • Java进阶之SPI机制的示例分析
    这篇文章将为大家详细讲解有关Java进阶之SPI机制的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一、前言SPI的英文全称为Service Provider Interface,字面意思为服务提...
    99+
    2023-06-15
  • 解析Golang锁的实现机制
    Golang锁的实现原理解析及代码示例引言:Go语言(Golang)是一门现代化、高效和强大的编程语言,广泛应用于网络编程和并发处理。并发是Go语言的核心特性之一,允许程序同时执行多个任务。然而,并发编程是一项复杂的任务,容易引发资源竞争问...
    99+
    2023-12-28
    解析 实现原理 Golang锁
  • 解析MySQL锁的实现机制
    MySQL 锁的实现原理解析引言:在并发访问数据库的环境中,为了保障数据的完整性和一致性,数据库系统需要实现锁机制。锁机制通过限制对共享资源的访问,确保不同的事务能够有序地访问和修改数据。MySQL作为一种常用的关系型数据库,也提供了多种锁...
    99+
    2023-12-21
    MySQL 锁实现原理
  • Apache Hudi灵活的Payload机制硬核解析
    1.摘要 Apache Hudi 的Payload是一种可扩展的数据处理机制,通过不同的Payload我们可以实现复杂场景的定制化数据写入方式,大大增加了数据处理的灵活性。Hudi ...
    99+
    2024-04-02
  • Java插件扩展机制之SPI的示例分析
    这篇文章给大家分享的是有关Java插件扩展机制之SPI的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。什么是SPISPI ,全称为 Service Provider Interface,是一种服务发现机制...
    99+
    2023-06-20
  • 一文带你了解Java中的SPI机制
    目录1: SPI机制简介2: SPI原理3: 使用场景4: 源码论证5: 实战6: 优缺点6.1 优点6.2 缺点1: SPI机制简介 SPI 全称是 ...
    99+
    2023-05-15
    Java SPI机制原理 Java SPI机制使用 Java SPI机制
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作