前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java中SPI机制

java中SPI机制

作者头像
代码改变世界-coding
发布2022-01-05 14:16:24
3580
发布2022-01-05 14:16:24
举报
文章被收录于专栏:java相关java相关

java SPI功能分享

1.什么是SPI

SPI,Service Provider Interface,是有java提供的一套用来被第三方实现或者扩展的API,本质是通过基于接口的编程+策略模式+配置文件实现动态加载。主要是被框架的开发人员使用,比如JDBC中驱驱动java.sql.Driver接口,不同的数据库厂商通过实现次接口完成对数据库的操作,mysql等数据库都有不同的实现类提供给用户,而Java的SPI机制可以为某个接口寻找具体的实现类。

2.实现SPI的几个约定

1、当服务提供者提供了接口的一种具体实现后,在jar包的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名;

2、接口实现类所在的jar包放在主程序的classpath中;

3、主程序通过java.util.ServiceLoder动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM;

4、SPI的实现类必须携带一个不带参数的构造方法;

3.SPI实现的例子

步骤一:定义接口
代码语言:javascript
复制
public interface LoadBalance {

    String selectServiceAddress(List<String> serviceAddresses);
}
步骤二:定义实现类
代码语言:javascript
复制
public class RandomLoadBalance implements LoadBalance {

    @Override
    public String selectServiceAddress(List<String> serviceAddresses) {
        Random random = new Random();
        return serviceAddresses.get(random.nextInt(serviceAddresses.size()));
    }

}
步骤三:添加配置文件

在resources文件目录下添加META-INF/services/目录,创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类。内容如下:

代码语言:javascript
复制
com.spi.javaspi.loadbalance.RandomLoadBalance
步骤四:使用ServiceLoader加载实现类
代码语言:javascript
复制
public static void main(String[] args) {

    ServiceLoader<LoadBalance> loadBalances = ServiceLoader.load(LoadBalance.class);
    Iterator<LoadBalance> matcherIter = loadBalances.iterator();
    while (matcherIter.hasNext()) {
        LoadBalance loadBalance = matcherIter.next();
        System.out.println(loadBalance.getClass().getName());
        System.out.println(loadBalance.selectServiceAddress(Arrays.asList("172.30.30.231", "172.30.30.232", "172.30.30.233")));
    }

}

4.JDBC中SPI使用分析

JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用java语言编写的类和接口组成。

JDBC操作数据库demo:
代码语言:javascript
复制
Connection con;

    public Connection getConnection() {
        try {
            con = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shop?characterEncoding=UTF-8", "root", "123456789");
            System.out.println("数据库连接成功");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return con;
    }

    public static void main(String[] args) throws Exception {
        JDBCTest c = new JDBCTest();
        Connection connection = c.getConnection();
        PreparedStatement statement = connection.prepareStatement("select * from Product");
        ResultSet resultSet = statement.executeQuery();
        while (resultSet.next()) {
            String productName = resultSet.getString("product_name");
            System.out.println("productName: " + productName);
        }
    }
相关类分析--DriverManager

静态代码块:

代码语言:javascript
复制
static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}

loadInitialDrivers方法:

代码语言:javascript
复制
private static void loadInitialDrivers() {
    String drivers;
    drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
        public String run() {
            return System.getProperty("jdbc.drivers");
        }
    });

    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {
            ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
            Iterator<Driver> driversIterator = loadedDrivers.iterator();
            while (driversIterator.hasNext()) {
                driversIterator.next();
            }

            return null;
        }
    });

    String[] driversList = drivers.split(":");
    for (String aDriver : driversList) {
        Class.forName(aDriver, true,
                ClassLoader.getSystemClassLoader());

    }
}

注册驱动方法:

代码语言:javascript
复制
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();

public static synchronized void registerDriver(java.sql.Driver driver,
        DriverAction da)
    throws SQLException {
    if(driver != null) {
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    } else {
        throw new NullPointerException();
    }
}
mysql中Driver实现类
代码语言:javascript
复制
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

获取连接方法:

代码语言:javascript
复制
private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {

    ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
    synchronized(DriverManager.class) {
        if (callerCL == null) {
            callerCL = Thread.currentThread().getContextClassLoader();
        }
    }
    SQLException reason = null;
    for(DriverInfo aDriver : registeredDrivers) {
        if(isDriverAllowed(aDriver.driver, callerCL)) {
            try {
                Connection con = aDriver.driver.connect(url, info);
                if (con != null) {
                    return (con);
                }
            } catch (SQLException ex) {
                if (reason == null) {
                    reason = ex;
                }
            }
        }
    }
}

5.优缺点分析

优点:通过SPI实现解耦,不需要改动源码就可以实现扩展

缺点:JDK标准的SPI会一次性实例化扩展点所有实现,如果有扩展实现则初始化很耗时(比如静态代码块初始化耗时长),如果没 用上也加载,则浪费资源

6.SPI机制的其他应用

Dubbo、spring、log4j等框架也大量使用了SPI机制

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-12-31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • java SPI功能分享
    • 1.什么是SPI
      • 2.实现SPI的几个约定
        • 3.SPI实现的例子
          • 4.JDBC中SPI使用分析
            • 5.优缺点分析
            相关产品与服务
            数据库
            云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档