[Spring ~松耦合的设计神器]`SPI`
Java SPI(Service Provider Interface)是一种Java的服务提供者接口机制。它允许在运行时动态加载实现服务接口的类。
基本概念
SPI 机制的基本思想是,定义一个服务接口,多个不同的实现类可以实现这个接口。然后,在classpath 路径下的META-INF/services目录中创建一个以服务接口的全限定名命名的文件,文件内容为实现类的全限定名。这样,当程序运行时,可以通过ServiceLoader工具类来加载所有实现了该服务接口的类。
使用Java SPI的好处是,可以在不修改源代码的情况下,通过配置更换实现类,实现程序的可扩展性。而不需要在代码中显式地引用实现类,从而实现了松耦合的设计。
SPI机制在Java中被广泛应用,例如JDBC中的Driver接口和Servlet容器中的Servlet接口等。通过SPI机制,可以在不同的场景中灵活地替换实现类,提供更多的选择和定制化的功能。同时,SPI机制也增加了程序的灵活性和可维护性。
最简单的实例
我们现在需要使用一个内容搜索接口,搜索的实现可能是基于文件系统的搜索,也可能是基于数据库的搜索,甚至是 rpc 搜索。
基本步骤
- 定义接口
- 在
META-INF/services目录下,新建接口全限定名(如果是内部接口或内部类需要使用$)的文件,然后在文件里面加上实现类。- 使用
ServiceLoader加载接口,进行使用
public class SpiApplication {
    public interface Search {
        List<String> searchDoc(String keyword);
    }
    public static class FileSearch implements Search{
        @Override
        public List<String> searchDoc(String keyword) {
            System.out.println("文件搜索 "+keyword);
            return null;
        }
    }
    public static class DatabaseSearch implements Search{
        @Override
        public List<String> searchDoc(String keyword) {
            System.out.println("数据搜索 "+keyword);
            return null;
        }
    }
    public static class RpcSearch implements Search{
        @Override
        public List<String> searchDoc(String keyword) {
            System.out.println("rpc搜索 "+keyword);
            return null;
        }
    }
    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");
        }
    }
}

 可以看到输出三行搜索结果,这就是因为 ServiceLoader.load(Search.class) 在加载某接口时,会去 META-INF/services 下找接口的全限定名文件,再根据里面的内容加载相应的实现类。
 这就是 spi 的思想,接口的实现由 provider 实现,provider 只用在提交的 jar 包里的 META-INF/services 下根据平台定义的接口新建文件,并添加进相应的实现类内容就好。
使用 jar 包通过 spi动态实现接口功能
 
新增 jar包,打包后加载到其他项目运行。
public interface Search {
    List<String> searchDoc(String keyword);
}
//-
public class DefaultSearch implements Search{
    @Override
    public List<String> searchDoc(String keyword) {
        System.out.println("default search");
        return null;
    }
}
//-
public class MainApplicaction {
    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");
        }
        System.out.println(">>>> jar main end");
    }
}

 然后 mvn install到本地,把 jar包导入到其他项目
 
package com.example.spi;
import com.example.MainApplicaction;
import com.example.Search;
import java.util.List;
public class SpiApplication {
    public static class FileSearch implements Search {
        @Override
        public List<String> searchDoc(String keyword) {
            System.out.println("文件搜索 "+keyword);
            return null;
        }
    }
    public static class DatabaseSearch implements Search{
        @Override
        public List<String> searchDoc(String keyword) {
            System.out.println("数据搜索 "+keyword);
            return null;
        }
    }
    public static class RpcSearch implements Search{
        @Override
        public List<String> searchDoc(String keyword) {
            System.out.println("rpc搜索 "+keyword);
            return null;
        }
    }
    public static void main(String[] args) {
        MainApplicaction.main(new String[0]);
    }
}
运行结果
文件搜索 hello world
数据搜索 hello world
rpc搜索 hello world
default search
>>>> jar main end
由此可以看到本项目的
META-INF/services的加载顺序在jar包前面。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!