SPI机制在我们的项目中应用了很多地方,在很多框架中也广泛使用,只是很多人没有感知到。比如为什么在项目中引入mysql-connector的jar包就可以直接连接MySQL数据库?本文将介绍SPI,谈谈Java、Spring、Dubbo中的SPI机制。SPISPI(ServiceProviderInterface),是一种服务发现机制。SPI的本质是在文件中配置接口实现类的全限定名,服务加载器读取配置文件加载对应接口的实现类。这样就可以在运行时获取接口的实现类。通过这个特性,我们可以方便的通过SPI机制为程序提供扩展功能。JavaSPIJavaSPI机制:JavaSPI是一种通过“基于接口编程+策略模式+配置文件”组合实现的动态加载机制。设计一个接口,在配置文件中写入接口的实现类,服务通过读取配置文件找到实现类,加载并实例化,然后使用。配置文件路径:类路径下的META-INF/services/配置文件名:接口全限定名配置文件内容:接口实现类的权限命名应用示例:1.自定义接口//interfacepublicinterfaceSuperman{voidintroduce();}//实现class1publicclassIronManimplementsSuperman{@Overridepublicvoidintroduce(){System.out.println("IamIronMan!");}}//Implementclass2publicclassCaptainAmericaimplementsSuperman{@Overridepublicvoidintroduce(){System.out.println("IamCaptainAmerica!");}}配置文件:test:publicstaticvoidmain(String[]args){ServiceLoaderserviceLoader=ServiceLoader.load(Superman.class);System.out.println("JavaSPI:");serviceLoader.forEach(Superman::introduce);}运行结果:2.java.sql.Driver接口的实现MySQL:你应该知道你看这个一开始的问题的答案是mysql-connector的jar表通过SPI实现了java的驱动接口,所以我们的服务在运行时可以拿到mysql的驱动类,从而连接到mysql。JavaSPI原理:JavaSPI是在ServiceLoader类中实现的:这里截取部分代码,有兴趣的同学可以自行阅读。获取前缀下的配置文件(含jar包):JavaSPI简介:JavaSPI机制:一种发现/寻找接口服务实现的机制。优点:核心思想:解耦,将第三方服务模块的组装控制逻辑与调用方的业务代码分离。可根据实际业务情况进行使用或扩展。缺点:1.接口实现类的获取方式不灵活。serviceLoader只能通过Iterator形式遍历,不能根据参数获取指定的实现类。2、资源浪费serviceLoader只能遍历获取、加载和实例化该接口的所有实现类。如果你不想使用一些实现类,它也会被加载和实例化,造成浪费。SpringSPI类似于JDKSPI。与JavaSPI相比的优势在于,SpringSPI在类路径下指定了一个配置文件为META-INF/spring.factories,所有的扩展点配置都放在一个文件中。配置文件的内容是key-value类型,key是接口的全限定名,value是实现类的全限定名,可以是多个。spring.factories文件示例:应用示例:以dubbo的使用为例:为什么要在项目中引入dubbojar包,并在application.yml中配置registry、provider等,以便直接使用dubbo通过使用dubbo的Serviceannotation和Referenceannotationserved?没有时间安静,但有人为你背负重担。“挑起担子的人”是“dubbo-spring-boot-starter”。其实就是利用了springSPI:以EnableAutoConfiguration的实现类DubboAutoConfiguration为例:在springboot启动过程中,在SpringFactoriesLoader.loadFactoryNames(类型,类加载器)。在实例化EnableAutoConfiguration的实现类时,会执行实现类dubboAutoConfiguration中的具体逻辑,启动dubbo服务器并注册到spring容器中。DubboAutoConfiguration的大致实现:读取配置文件中的配置项值(配置项:DubboConfigConfiguration)生成多个配置bean,扫描dubbo@Service和@Reference注解类,生成对应的bean。其实我们使用的很多第三方依赖包都是用的SpringSPI,比如dubbo、mybatis、redisson等。DubboSPIdubbo的Filter、Protocol、Cluster、LoadBalance等都是通过SPI进行扩展和加载的。特点:1.dubboSPI为每一个扩展点(接口)设置一个单独的文件,文件名是接口的全限定名。如org.apache.dubbo.rpc.Filter,org.apache.dubbo.rpc.Protocol,org.apache.dubbo.rpc.cluster.LoadBalance等dubboSPI配置文件示例:2.支持“的概念alias”,可以通过别名获取扩展点的某个实现。配置文件的内容是key-value类型,key是别名,value是实现类的全限定名。仅使用指定的过滤器,不实例化其他过滤器。3.支持Dubbo内部依赖注入DubboIOC通过setter方法实现依赖注入。Dubbo首先通过反射获取实例的所有方法,然后遍历方法列表,检测方法名是否具有setter方法的特征。如果是,则通过ObjectFactory获取依赖对象,最后通过反射调用setter方法设置对目标对象的依赖。实现:dubboSPI的实现在ExtensionLoader类中。以获取所有dubbofilters为例:1.首先获取FilterExtensionLoader.getExtensionLoader(Filter.class)的ExtensionLoader2.通过ExtensionLoader从配置文件中加载所有的扩展类加载工程和以下目录下的配置文件jar包下:Configuration文件名是接口的全限定名。3、读取配置文件时,根据'='的边界来确认键值对。由此得到“配置项名称”到“配置类”的映射关系表。4、进程中使用了多个缓存,提高了性能。缓存扩展类对应的ExtensionLoader等。获取别名后-实现类的全限定名,可以直接通过别名获取指定的扩展类。Java、Spring、DubboSPI对比