java学习-原生服务发现-SPI

SPI : service provider interface, 其实际是java提供的一套用于被第三方实现或扩展的API

其体现了 java 基于接口编程的思想, 可以搭配策略模式以及配置文件实现自定义扩展

 

例如 :数据库驱动,日志,spring,以及dubbo/druid等

 

关于SPI 相关配置文件

根据 java.util.ServiceLoader 中的注释, 首先需要创建一个 待扩展类全名称的文件, 对于 待扩展类建议要求使用 interface / abstract class ,并不推荐使用 concrete class;

对于文件中的内容要求为当前服务提供者的子级(继承/实现),且要求必须存在一个公共的无参构造函数支持实例化

对于文件所处路径必须为 META-INF/services 路径中

 

具体使用

使用 java.util.ServiceLoader#load(java.lang.Class<S>), 其还存在一个重载方法支持传递自定义classLoader

通过传递serviceProvider 类型来创建一个ServiceLoader对象, 通过获取其迭代器,遍历迭代器来实现扩展服务加载;

对于服务实现类的加载是在调用 迭代时生成的, 第一次加载时主要是利用到了反射, 当第一次加载完成后会存储到本地缓存中

 

代码示例

自定义接口作为 serviceProvider

/*
 * Copyright (c) 2020, guoxing, Co,. Ltd. All Rights Reserved
 */
package com.xingguo.spi;

/**
 * Animal
 *
 * @author guoxing
 * @date 2020/12/20 12:17 PM
 * @since
 */
public interface Animal {

    void eat();
}

 

自定义相关实现类

/*
 * Copyright (c) 2020, guoxing, Co,. Ltd. All Rights Reserved
 */
package com.xingguo.spi;

/**
 * Cat
 *
 * @author guoxing
 * @date 2020/12/20 12:18 PM
 * @since
 */
public class Cat implements Animal {
    @Override
    public void eat() {
        System.out.println("cat eat fish!!!");
    }
}
/*
 * Copyright (c) 2020, guoxing, Co,. Ltd. All Rights Reserved
 */
package com.xingguo.spi;

/**
 * Human
 *
 * @author guoxing
 * @date 2020/12/20 12:19 PM
 * @since
 */
public class Human implements Animal {
    @Override
    public void eat() {
        System.out.println("human eat anything what they can eat");
    }
}

 

自定义spi文件(无后缀名)

文件路径 META-INF/services/com.xingguo.spi.Animal

文件中的内容, 使用"#"表示注释

#当前文件中也可以不存在任何内容
#要求当前concrete class 必须和当前文件名称所对应的类 存在层级关系,否则运行时会抛出错误
#且要求实现类中必须存在一个公共的无参构造函数
com.xingguo.spi.Cat
com.xingguo.spi.Human

 

测试类

/*
 * Copyright (c) 2020, guoxing, Co,. Ltd. All Rights Reserved
 */
package com.xingguo.spi;

import java.util.Iterator;
import java.util.ServiceLoader;

/**
 * AnimalSPIDemo
 * {@link ServiceLoader}
 * {@link Animal src/META-INF/services/com.xingguo.spi.Animal}
 *
 * @author guoxing
 * @date 2020/12/20 12:20 PM
 * @since
 */
public class AnimalSPIDemo {
    public static void main(String[] args) {
        ServiceLoader<Animal> animals = ServiceLoader.load(Animal.class, Thread.currentThread().getContextClassLoader());
        Iterator<Animal> iterator = animals.iterator();
        while (iterator.hasNext()) {
            Animal next = iterator.next();
            next.eat();
        }
    }
}

 

利用SPI实现 数据库 驱动加载

通过spi的方式来替代直接使用 Class.forName 加载的方式

要求工程引入相关数据库厂商的驱动jar包

spi文件定义

文件路径 META-INF/services/java.sql.Driver
文件中的内容

# mysql driver
com.mysql.cj.jdbc.Driver

 

相关测试类

/*
 * Copyright (c) 2020, guoxing, Co,. Ltd. All Rights Reserved
 */
package com.xingguo.spi.driver;

import java.sql.Driver;
import java.util.Iterator;
import java.util.ServiceLoader;

/**
 * SqlDriverDemo
 *
 * @author guoxing
 * @date 2020/12/20 1:04 PM
 * @since
 */
public class SqlDriverDemo {

    public static void main(String[] args) {
        ServiceLoader<Driver> load = ServiceLoader.load(Driver.class);
        Iterator<Driver> iterator = load.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next()); // 输出的结果为 com.mysql.cj.jdbc.Driver@对象内存地址
        }
    }
}

 

posted @ 2020-12-20 13:16  郭星  阅读(167)  评论(0)    收藏  举报