Java面试题-Java核心基础-第十二天(SPI机制)

目录

一、什么是SPI机制

二、SPI机制的作用

三、SPI的一些应用

四、 例子


一、什么是SPI机制

SPI因为service provider interface 意为:服务提供者的接口

就是为服务提供者提供的接口,就是设计一套接口规范,然后不同的服务提供者去进行相应的实现

这个SPI机制依赖于Java库中的ServiceLoad类加载实现类才能实现这样一种机制

过程:

1. 需要提供一个接口

2. 服务提供者去实现接口,就有会对应的实现类

上面这样也能直接用,但是多个实现就有多个jar包,其实服务调用方和服务的提供者还是存在一定的耦合的,我想换一种,那里就要另外编写一种,存在耦合

ServiceLoad就可以加载出所有的实现类,得到实现类的集合。

说一下这个使用的步骤:

1.在接口的提供者这一方项目中的resource目录下面,创建好一个和接口全类名一模一样的文件,然后在这个文件中写好实现类的全类名

2. 然后就能使用ServiceLoad加载出文件中写的这些实现类,获取到这些实现类了,在接口的提供者实际上就能真正的使用自己写的实现类了,因为我仅仅实现接口有一个接口实现类是不行的,其实就是接口提供者这边其实是导入了那套接口规范,那套结构中其实是里面使用到了ServiceLoad去加载类的,然后再完成操作。其实实现类只是工具,具体怎样使用这个工具由定义接口规范的人说的,现在接口提供者这一方已经有了实现类了,有了接口规范了(其实并不是真正的是接口规范,而是说操作接口方法的一段代码逻辑)。现在的问题就是说到底怎样才能让真正的使用到这些实现类中的具体实现。当然不能直接new的方式,这样就写死了,就只能一种实现类。那有什么办法,就是说不通过new的方式,然后也能拿到实现类,并且这里是可以拿到所有的实现类对象,只有这样,才真正的解耦合。

其实:

接口规范这一边要干的活:

1. 定义接口  其实就是定义好接口中的方法  想清楚到底要一些什么样的方法,大致是想要干什么?

2. 定义操作接口方法的一套流程 通过这个流程就能真正的干活

服务提供者这一方需要干的活:

其实服务提供者的这一边它需要导入  这一套接口规范以及这一套方法流程 其实就是得到接口规范的这一方的全部  所以通常来说接口提供者这一方,需要导入接口规范的jar包

1. 写实现类

2. 项目中的resource目录下面,创建好一个和接口全类名一模一样的文件,然后在这个文件中写好实现类的全类名

只要这样  就可以通过ServiceLoad感知到这些实现类  将“实现类”填充到那个方法流程框架中

然后就能够真正的正常执行那一套流程了

因此我们现在只需要将服务提供者打一个jar包,然后我们调用方的这一边就导入这个jar包就能使用了  就能使用那套流程了   

二、SPI机制的作用

就是解耦合

它的强大之处在于我代码真的可以完完全全的写死,完完全全不需要任何的改动,就能直接使用到接口提供者给我们实现的方法

三、SPI的一些应用

jdbc 也是一个接口规范一方定义好一写接口 java.sql.Driver 写好这个里面的方法 

Connection connect(String url, java.util.Properties info)
throws SQLException;

java.sql.* 这个包下定义了接口  Connnct PrepareStatement ResultSet

然后不同的数据库厂商就根据这些接口写好对应的实现类

在之前还没有ServiceLoad这个类  所以它能做的就只能实现好那些接口  但是呢 其实还是不能直接用,那怎样才能用?那就需要服务调用 方完成加载驱动这一步的  只有完成了这一步相当于才真正的将实现类导入过来了 

现在有了ServiceLoad这个类,就可以调用它的load方法,然后就能自动的加载这个接口下面的实现类  所以现在数据库厂商那边就可以直接注册这个实现类了 通过规定的步骤  就是写一个文件

既然现在数据库厂商那边 相当于都把事情做好了,因此我们调用方这边就可以不需要再去完成注册驱动的这一步了,这一步可以省略了  这是JDBC4.0之后支持的

关键:

​
ServiceLoader<Driver> s = ServiceLoader.load(Driver.class);
ServiceLoader<Connection> s = ServiceLoader.load(Connection.class);
.....

​

然后在数据库服务器厂商那边在项目的resouces目录下面写好:

一个 名为java.sql.Driver的文件 然后里面写Driver实现类的全类名

......

另外还有日志框架也是利用SPI机制

四、 例子

我们现在需要使用一个内容搜索接口,搜索的实现可能是基于文件系统的搜索,也可能是基于数据库的搜索。

  • 先定义好接口
  • public interface Search {
        public List<String> searchDoc(String keyword);   
    }

  • 文件搜索实现
  • public class FileSearch implements Search{
        @Override
        public List<String> searchDoc(String keyword) {
            System.out.println("文件搜索 "+keyword);
            return null;
        }
    }
    

  • 数据库搜索实现
  • public class DatabaseSearch implements Search{
        @Override
        public List<String> searchDoc(String keyword) {
            System.out.println("数据搜索 "+keyword);
            return null;
        }
    }
    

  • resources 接下来可以在resources下新建META-INF/services/目录,然后新建接口全限定名的文件:com.cainiao.ys.spi.learn.Search,里面加上我们需要用到的实现类
  • com.cainiao.ys.spi.learn.FileSearch

  • 测试方法
  • public class TestCase {
        public static void main(String[] args) {
            ServiceLoader<Search> s = ServiceLoader.load(Search.class);
            Iterator<Search> iterator = s.iterator();
            while (iterator.hasNext()) {
               Search search =  iterator.next();
               search.searchDoc("hello world");
            }
        }
    }
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
java面试真的很多,下面我来回答一个有关多线程的问。 在Java中实现多线程有两种方式,一种是继承Thread类,另一种是实现Runnable接口。这两种方式有何区别? 继承Thread类的方式是直接定义一个类继承Thread,并重写它的run()方法。然后创建该类的对象,并调用对象的start()方法来启动线程。这种方式简单直接,但因为Java是单继承的,所以如果某个类已经继承了其他类,就不能再直接继承Thread类实现多线程。 实现Runnable接口的方式是定义一个类实现Runnable接口,并实现其唯一的抽象方法run()。然后创建Thread类的对象,将实现了Runnable的对象作为参数传递给Thread类的构造方法。最后调用Thread对象的start()方法来启动线程。这种方式灵活性更大,因为Java允许一个类实现多个接口,所以即使某个类已经继承了其他类,仍然可以通过实现Runnable接口来实现多线程。 另一个区别在于资源共享的问。继承Thread类的方式,不管是数据还是方法,都是线程自己拥有的,不存在共享的情况。而实现Runnable接口的方式,多个线程可以共享同一个对象的数据和方法,因为多个线程共同操作的是同一个Runnable对象。 总结来说,继承Thread类方式简单直接,但只能通过单继承来实现多线程;实现Runnable接口方式更灵活,可以解决单继承的限制,并且多个线程可以共享同一个Runnable对象的数据和方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值