您好,登录后才能下订单哦!
这篇文章将为大家详细讲解有关Java进阶之SPI机制的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
SPI的英文全称为Service Provider Interface,字面意思为服务提供者接口,它是jdk提供给“服务提供厂商”或者“插件开发者”使用的接口。
在面向对象的设计中,模块之间我们一般会采取面向接口编程的方式,而在实际编程过程过程中,API的实现是封装在jar中,当我们想要换一种实现方法时,还要生成新的jar替换以前的实现类。而通过jdk的SPI机制就可以实现,首先不需要修改原来作为接口的jar的情况下,将原来实现的那个jar替换为另外一种实现的jar即可。
总结一下SPI的思想:在系统的各个模块中,往往有不同的实现方案,例如日志模块的方案、xml解析的方案等,为了在装载模块的时候不具体指明实现类,我们需要一种服务发现机制,java spi就提供这样一种机制。有点类似于IoC的思想,将服务装配的控制权移到程序之外,在模块化设计时尤其重要。
顺便提一下,Java SPI机制在很多大型中间件吗,例如Dubbo中均有采用,属于高级Java开发的进阶必备知识点,务必要求掌握。
定义服务的通用接口,针对通用的服务接口,提供具体的实现类。
1.在jar包(服务提供者)的META-INF/services/目录中,新建一个文件,文件名为SPI接口的"全限定名"。 文件内容为该接口的具体实现类的"全限定名"
2.将spi所在jar放在主程序的classpath中
3.服务调用方使用java.util.ServiceLoader
去动态加载具体的实现类到JVM中
java.sql.Driver的spi实现,有mysql驱动、oracle驱动等。以mysql为例,实现类是com.mysql.jdbc.Driver,在mysql-connector-java-5.1.6.jar中,我们可以看到有一个META-INF/services目录,目录下有一个文件名为java.sql.Driver的文件,其中的内容是com.mysql.jdbc.Driver。
在我们使用JDBC获取连接时,我们通常会调用DriverManager.getConnection()
方法获取连接对象,而在Driver类初始化时会使用ServiceLoader
动态获取classpath下“注册”的驱动实现:
static { loadInitialDrivers(); println("JDBC DriverManager initialized"); } private static void loadInitialDrivers() { ..... AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { //使用ServiceLoader动态加载具体的驱动实现类 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator<Driver> driversIterator = loadedDrivers.iterator(); try{ while(driversIterator.hasNext()) { driversIterator.next(); } } catch(Throwable t) { // Do nothing } return null; } }); ..... }
slf4j是一个典型的门面接口,早起我们使用log4j作为日记记录框架,我们需要同时引入slf4j和log4j的依赖。后面比较流行logback,我们也想要把项目切换到logback上来,此时利用SPI的机制,我们只需要把log4j的jar包替换为logback的jar包就可以了。
在log4j-to-slf4j.jar
中我们可以看到前面提到的服务提供方的SPI接口声明:
这样我们只需要将log4j-to-slf4j.jar
引入classpath,slf4j就能够获取到org.apache.logging.slf4j.SLF4JProvider
作为实现类。
模块依赖关系:
在spi-operate-service
中定义INumOperate
接口:
package cn.bigcoder.spi.operate.service; /** * @author: Jindong.Tian * @date: 2020-11-29 * @description: **/ public interface INumOperate { int exec(int num1, int num2); }
在spi-operate-add
中定义加法实现:
package cn.bigcoder.spi.operate.add; import cn.bigcoder.spi.operate.service.INumOperate; /** * @author: Jindong.Tian * @date: 2020-11-29 * @description: **/ public class NumAddOperateImpl implements INumOperate { public int exec(int num1, int num2) { return num1 + num2; } }
在resource/METAINF/resoures
目录下创建cn.bigcoder.spi.operate.service.INumOperate
文件:
cn.bigcoder.spi.operate.add.NumAddOperateImpl
在spi-operate-multiplication
模块中定义乘法的实现:
package cn.bigcoder.spi.operate.multiplication; import cn.bigcoder.spi.operate.service.INumOperate; /** * @author: Jindong.Tian * @date: 2020-11-29 * @description: **/ public class NumMultiOperateImpl implements INumOperate { public int exec(int num1, int num2) { return num1 * num2; } }
同样的在resource/METAINF/resoures
目录下创建cn.bigcoder.spi.operate.service.INumOperate
文件:
cn.bigcoder.spi.operate.multiplication.NumMultiOperateImpl
在spi-operate-consumer
模块中,我们编写测试类获取classpath中INumOperate
实现类:
package cn.bigcoder.spi.operate; import cn.bigcoder.spi.operate.service.INumOperate; import org.junit.Test; import java.util.Iterator; import java.util.ServiceLoader; /** * @author: Jindong.Tian * @date: 2020-11-29 * @description: **/ public class INumOperateTest { @Test public void test() { // SPI机制,寻找所有的实现类,顺序执行 ServiceLoader<INumOperate> loader = ServiceLoader.load(INumOperate.class); Iterator<INumOperate> iterator = loader.iterator(); if (iterator.hasNext()) { INumOperate numOperate = iterator.next(); System.out.println(numOperate.exec(1, 2)); } else { throw new RuntimeException("classpath中未找到cn.bigcoder.spi.operate.INumOperate实现类"); } } }
此时如果我们在spi-operate-consumer
中引入spi-operate-add
,则测试方法执行求和操作;如果引入spi-operate-multiplication
,则测试方法执行乘法操作。
Java主要应用于:1. web开发;2. Android开发;3. 客户端开发;4. 网页开发;5. 企业级应用开发;6. Java大数据开发;7.游戏开发等。
关于“Java进阶之SPI机制的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。