Rumah  >  Artikel  >  Java  >  Apakah mekanisme SPI di Jawa

Apakah mekanisme SPI di Jawa

王林
王林ke hadapan
2023-05-17 23:40:553156semak imbas

1: Pengenalan kepada mekanisme SPI

SPI Nama penuh ialah Service Provider Interface, yang merupakan JDK mekanisme pemuatan dinamik terbina dalam untuk melaksanakan titik sambungan melalui teknologi SPI kita boleh secara dinamik mendapatkan kelas Pelaksanaan antara muka, tidak perlu menciptanya sendiri. Ini bukan teknologi khas, hanya konsep reka bentuk.

2: Prinsip SPI

Apakah mekanisme SPI di Jawa

Java SPI sebenarnya adalah mekanisme pemuatan dinamik yang dilaksanakan berdasarkan pengaturcaraan antara muka + mod strategi + gabungan fail konfigurasi.

Selalunya terdapat banyak penyelesaian pelaksanaan yang berbeza untuk setiap abstraksi reka bentuk sistem Dalam reka bentuk berorientasikan objek, biasanya disyorkan bahawa modul diprogramkan berdasarkan antara muka dan kelas pelaksanaan tidak boleh dikodkan dengan keras antara modul. Jika kelas pelaksanaan tertentu dirujuk dalam kod, maka prinsip kebolehpasangan dilanggar. Untuk melaksanakan penggantian, kod tersebut perlu diubah suai. Mekanisme penemuan perkhidmatan diperlukan untuk membolehkan pemasangan modul tanpa menyatakannya secara dinamik dalam program.

Java SPI menyediakan mekanisme untuk mencari pelaksanaan perkhidmatan yang berkaitan dengan antara muka. Dalam reka bentuk modular, mekanisme yang serupa dengan idea IOC digunakan secara meluas, iaitu, kawalan pemasangan komponen dipindahkan ke luar program. Jadi idea teras SPI adalah penyahgandingan.

3: Senario Penggunaan

Pemanggil mendayakan, melanjutkan atau menggantikan strategi pelaksanaan rangka kerja mengikut keperluan sebenar

Berikut ialah beberapa senario di mana mekanisme ini digunakan

  • Pemandu JDBC, memuatkan kelas pemacu untuk pangkalan data yang berbeza

  • Spring menggunakan banyak SPI, seperti: pelaksanaan ServletContainerInitializer dalam servlet3.0 spesifikasi, SPI Penukaran Jenis automatik (SPI Penukar, SPI Pemformat), dsb.

  • Dubbo juga menggunakan SPI secara meluas untuk melanjutkan rangka kerja, tetapi ia merangkumi SPI asli yang disediakan oleh pengguna Java untuk melanjutkan pelaksanaan antara muka Penapis

  • Tomcat memuatkan kelas yang perlu dimuatkan di bawah META-INF/services

  • Gunakan @ Anotasi SpringBootApplication dalam projek SpringBoot , konfigurasi automatik akan bermula dan konfigurasi permulaan akan mengimbas kelas konfigurasi di bawah META-INF/spring.factories

4: Demonstrasi kod sumber

4.1 Aplikasi Panggil kaedah ServiceLoader.load

Dalam kaedah ServiceLoader.load, mula-mula buat ServiceLoader baharu dan nyatakan pembolehubah ahli dalam kelas

    private static final String PREFIX = "META-INF/services/";


  private ServiceLoader(Class<S> svc, ClassLoader cl) {
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
        reload();
    }

	/** 
     * 
     * 在调用该方法之后,迭代器方法的后续调用将延迟地从头开始查找和实例化提供程序,就像新创建的加载程序所做的		  那样
     */
   public void reload() {
        providers.clear(); //清除此加载程序的提供程序缓存,以便重新加载所有提供程序。
        lookupIterator = new LazyIterator(service, loader);
    }

	private class LazyIterator implements Iterator<S>{

        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        Iterator<String> pending = null;
        String nextName = null;


        private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                    //找到配置文件
                    String fullName = PREFIX + service.getName();
                    //加载配置文件中的内容
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x);
                }
            }
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                //解析配置文件
                pending = parse(service, configs.nextElement());
            }
            //获取配置文件中内容
            nextName = pending.next();
            return true;
        }
    }

		/** 
     	* 
     	*  通过反射 实例化配置文件中的具体实现类
    	 */
		private S nextService() {
            if (!hasNextService())
                throw new NoSuchElementException();
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
                fail(service,
                     "Provider " + cn + " not found");
            }
            if (!service.isAssignableFrom(c)) {
                fail(service,
                     "Provider " + cn  + " not a subtype");
            }
            try {
                S p = service.cast(c.newInstance());
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated",
                     x);
            }
            throw new Error();          // This cannot happen
        }

5: Pertempuran sebenar

Langkah 1 Cipta kelas berikut

public interface IService {

    /**
     * 获取价格
     * @return
     */
    String getPrice();

    /**
     * 获取规格信息
     * @return
     */
    String getSpecifications();
}
public class GoodServiceImpl implements IService {

    @Override
    public String getPrice() {
        return "2000.00元";
    }

    @Override
    public String getSpecifications() {
        return "200g/件";
    }
}
public class MedicalServiceImpl implements IService {

    @Override
    public String getPrice() {
        return "3022.12元";
    }

    @Override
    public String getSpecifications() {
        return "30粒/盒";
    }
}

Langkah 2, buat direktori /META-INF/services di bawah src/main/resources/ dan tambah fail yang dinamakan sempena antara muka org.example.IService.txt. Kandungan ialah kelas pelaksanaan yang akan digunakan. Data yang perlu saya masukkan adalah seperti berikut

org.example.GoodServiceImpl
org.example.MedicalServiceImpl

Langkah 3, gunakan ServiceLoader untuk memuatkan pelaksanaan yang dinyatakan dalam fail konfigurasi.

public class Main {
    public static void main(String[] args) {
        final ServiceLoader<IService> serviceLoader = ServiceLoader.load(IService.class);
        serviceLoader.forEach(service -> {
            System.out.println(service.getPrice() + "=" + service.getSpecifications());
        });
    }
}

Output:

2000.00 yuan=200g/keping
3022.12 yuan=30 kapsul/kotak

6: Kelebihan dan Kekurangan

6: Kelebihan dan Kekurangan >

6.1 Kelebihan

Penyahgandingan memisahkan logik kawalan pemasangan modul perkhidmatan pihak ketiga daripada kod perniagaan pemanggil dan bukannya menggabungkannya bersama-sama Aplikasi boleh mendayakan sambungan rangka kerja atau menggantikan rangka kerja mengikut keadaan perniagaan sebenar komponen. Berbanding dengan menggunakan pakej balang antara muka untuk modul perkhidmatan pihak ketiga untuk melaksanakan antara muka, kaedah SPI membenarkan rangka kerja sumber tidak perlu mengambil berat tentang laluan kelas pelaksanaan antara muka

6.2 Kelemahan

  • Atas ialah kandungan terperinci Apakah mekanisme SPI di Jawa. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

    Kenyataan:
    Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam