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.
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.
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.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 }
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
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
Atas ialah kandungan terperinci Apakah mekanisme SPI di Jawa. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!