Java SPI解析

SPI

Service Provider Interface.是JDK层面上的一种服务注册发现机制。可以实现依赖之间的解耦。来看一下具体的用法。使用SPI一般分为以下四个步骤

定义接口

定义一个接口,比如定义一个宠物的接口:

package com.yg.myint;

/**
 * @author yg
 */
public interface Pet {
    /**
     * Shot
     */
    void Shot();
}

实现接口

定义接口之后,可以写两个实现类。
实现一:

package com.yg.SPI;

import com.yg.myint.Pet;

/**
 * @author yg
 */
public class CatImpl implements Pet {

    @Override
    public void Shot() {
        System.out.println("mmmm");
    }
}

实现二:

package com.yg.SPI;

import com.yg.myint.Pet;

/**
 * @author yg
 */
public class DogImpl implements Pet {
    @Override
    public void Shot() {
        System.out.println("wwww");
    }
}

写实现路径

在完成实现类以后,需要在classpath下面,新建一个META-INF文件,然后新建serivces文件夹,在文件夹下面一个文件,文件名为接口的全限定名:在这里插入图片描述
这个权限定名的获取很简单,可以考虑直接:
在这里插入图片描述
直接copy引用即可。
文件的内容写实现的全限定名:
在这里插入图片描述
获取实现的全限定名也是可以直接copy reference。

测试

直接写个main函数测试一下:

package com.yg.SPI;

import com.yg.myint.Pet;

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ServiceLoader;

/**
 * @author yg
 */
public class Main {
    public static void main(String[] args) {
        ServiceLoader<Pet>myPets=ServiceLoader.load(Pet.class);
        for(Pet p:myPets){
            p.Shot();
        }
    }
}

在这里插入图片描述
结果没啥问题。

原理分析

SPI提供了一种动态的注入实现的机制,其实和Spring优点类似。打开ServiceLoader源码看一下:
进来首先看到:
在这里插入图片描述
在第189行定义了一个PREFIX,这也就是为什么我们要把实现的配置写到这个路径下,因为默认加载这个下面的配置。然后后面就是读取文件内容,按行读取的。这一点在251行有实现:
在这里插入图片描述
并且这个函数可以继续看:
在第259行:
在这里插入图片描述
可以发现,其实函数的全限定名字取得是(0,#)这中间的所有字符串。那么也就是说其实我们的文件这样写也可以:
在这里插入图片描述
可以试一下,确实可以。虽然我也没咋地看出来为什么使用#做分割。
到这里基本就明白了咋回事了:
SPI其实就是解耦了接口的实现,可以实现接口的注入。这个注入方式就是通过META-INF下的一个配置文件实现的

实例分析

使用SPI的典型例子,莫过于JDBC的驱动了。在加载JDBC驱动是,我们经常这样写:

public class Main {
    public static void main(String[] args) {
        String url = "";
        String userName = "";
        String pwd = "";
        try {
            Class.forName("com.mysql.jdbc.Driver");
            Connection connection = DriverManager.getConnection(url, userName, pwd);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这里显式的加载了mysql驱动: Class.forName(“com.mysql.jdbc.Driver”);
但是在jdbc高版本之后(我忘了是哪个版本??),就不用了,直接可以这样写:在这里插入图片描述

不用加载驱动的原因

打开driverManager,在第101行可以看到:
在这里插入图片描述
在静态代码块里面load了Driver,看一下这个load的实现:
在这里插入图片描述
在第585-589行,加载了driver。打开MySQL的配置文件可以看到:
在这里插入图片描述
在META-INF下确实有文件在这,打开文件看看:
在这里插入图片描述
这个驱动就是我们平时写的驱动的Name。到这里基本就解答了我们的疑惑,为什么不用显式的加载驱动,其实这里已经加载好了。

SPI

SPI其实是一种思想,动态的注入接口的实现。这种思想不仅仅可以用在本地,在分布式状态下也是可以借鉴。把实现下发到各个业务方,自己不用关心具体实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值