真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

如何理解Java中的SPI機(jī)制

本篇內(nèi)容介紹了“如何理解Java中的SPI機(jī)制”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

成都創(chuàng)新互聯(lián)專注于南岔企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城網(wǎng)站建設(shè)。南岔網(wǎng)站建設(shè)公司,為南岔等地區(qū)提供建站服務(wù)。全流程按需定制開發(fā),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,成都創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)

SPI的概念

SPI在Java中的全稱為Service Provider  Interface,是JDK內(nèi)置的一種服務(wù)提供發(fā)現(xiàn)機(jī)制,是Java提供的一套用來被第三方實(shí)現(xiàn)或者擴(kuò)展的API,它可以用來啟用框架擴(kuò)展和替換組件。

JAVA SPI = 基于接口的編程+策略模式+配置文件的動(dòng)態(tài)加載機(jī)制

SPI的使用場(chǎng)景

Java是一種面向?qū)ο笳Z言,雖然Java8開始支持函數(shù)式編程和Stream,但是總體來說,還是面向?qū)ο蟮恼Z言。在使用Java進(jìn)行面向?qū)ο箝_發(fā)時(shí),一般會(huì)推薦使用基于接口的編程,程序的模塊與模塊之前不會(huì)直接進(jìn)行實(shí)現(xiàn)類的硬編碼。而在實(shí)際的開發(fā)過程中,往往一個(gè)接口會(huì)有多個(gè)實(shí)現(xiàn)類,各實(shí)現(xiàn)類要么實(shí)現(xiàn)的邏輯不同,要么使用的方式不同,還有的就是實(shí)現(xiàn)的技術(shù)不同。為了使調(diào)用方在調(diào)用接口的時(shí)候,明確的知道自己調(diào)用的是接口的哪個(gè)實(shí)現(xiàn)類,或者說為了實(shí)現(xiàn)在模塊裝配的時(shí)候不用在程序里動(dòng)態(tài)指明,這就需要一種服務(wù)發(fā)現(xiàn)機(jī)制。Java中的SPI加載機(jī)制能夠滿足這樣的需求,它能夠自動(dòng)尋找某個(gè)接口的實(shí)現(xiàn)類。

大量的框架使用了Java的SPI技術(shù),如下:

(1)JDBC加載不同類型的數(shù)據(jù)庫驅(qū)動(dòng) (2)日志門面接口實(shí)現(xiàn)類加載,SLF4J加載不同提供商的日志實(shí)現(xiàn)類 (3)Spring中大量使用了SPI

對(duì)servlet3.0規(guī)范

對(duì)ServletContainerInitializer的實(shí)現(xiàn)

自動(dòng)類型轉(zhuǎn)換Type Conversion SPI(Converter SPI、Formatter SPI)等

(4)Dubbo里面有很多個(gè)組件,每個(gè)組件在框架中都是以接口的形成抽象出來!具體的實(shí)現(xiàn)又分很多種,在程序執(zhí)行時(shí)根據(jù)用戶的配置來按需取接口的實(shí)現(xiàn)

SPI的使用

當(dāng)服務(wù)的提供者,提供了接口的一種實(shí)現(xiàn)后,需要在Jar包的META-INF/services/目錄下,創(chuàng)建一個(gè)以接口的名稱(包名.接口名的形式)命名的文件,在文件中配置接口的實(shí)現(xiàn)類(完整的包名+類名)。

當(dāng)外部程序通過java.util.ServiceLoader類裝載這個(gè)接口時(shí),就能夠通過該Jar包的META/Services/目錄里的配置文件找到具體的實(shí)現(xiàn)類名,裝載實(shí)例化,完成注入。同時(shí),SPI的規(guī)范規(guī)定了接口的實(shí)現(xiàn)類必須有一個(gè)無參構(gòu)造方法。

SPI中查找接口的實(shí)現(xiàn)類是通過java.util.ServiceLoader,而在java.util.ServiceLoader類中有一行代碼如下:

// 加載具體實(shí)現(xiàn)類信息的前綴,也就是以接口命名的文件需要放到Jar包中的META-INF/services/目錄下 private static final String PREFIX = "META-INF/services/";

這也就是說,我們必須將接口的配置文件寫到Jar包的META/Services/目錄下。

SPI實(shí)例

這里,給出一個(gè)簡(jiǎn)單的SPI使用實(shí)例,演示在Java程序中如何使用SPI動(dòng)態(tài)加載接口的實(shí)現(xiàn)類。

注意:實(shí)例是基于Java8進(jìn)行開發(fā)的。

1.創(chuàng)建Maven項(xiàng)目

在IDEA中創(chuàng)建Maven項(xiàng)目spi-demo,如下:

如何理解Java中的SPI機(jī)制

2.編輯pom.xml

   spi-demo io.binghe.spi jar 1.0.0-SNAPSHOT 4.0.0                             org.springframework.boot             spring-boot-maven-plugin                               org.apache.maven.plugins             maven-compiler-plugin             3.6.0                              1.8                 1.8                              

3.創(chuàng)建類加載工具類

在io.binghe.spi.loader包下創(chuàng)建MyServiceLoader,MyServiceLoader類中直接調(diào)用JDK的ServiceLoader類加載Class。代碼如下所示。

package io.binghe.spi.loader;   import java.util.ServiceLoader;   /**  * @author binghe  * @version 1.0.0  * @description 類加載工具  */ public class MyServiceLoader {       /**      * 使用SPI機(jī)制加載所有的Class      */     public static  ServiceLoader loadAll(final Class clazz) {         return ServiceLoader.load(clazz);     } }

4.創(chuàng)建接口

在io.binghe.spi.service包下創(chuàng)建接口MyService,作為測(cè)試接口,接口中只有一個(gè)方法,打印傳入的字符串信息。代碼如下所示:

package io.binghe.spi.service;   /**  * @author binghe  * @version 1.0.0  * @description 定義接口  */ public interface MyService {       /**      *  打印信息      */     void print(String info); }

5.創(chuàng)建接口的實(shí)現(xiàn)類

(1)創(chuàng)建第一個(gè)實(shí)現(xiàn)類MyServiceA

在io.binghe.spi.service.impl包下創(chuàng)建MyServiceA類,實(shí)現(xiàn)MyService接口。代碼如下所示:

package io.binghe.spi.service.impl; import io.binghe.spi.service.MyService;   /**  * @author binghe  * @version 1.0.0  * @description 接口的第一個(gè)實(shí)現(xiàn)  */ public class MyServiceA implements MyService {     @Override     public void print(String info) {         System.out.println(MyServiceA.class.getName() + " print " + info);     } }

(2)創(chuàng)建第二個(gè)實(shí)現(xiàn)類MyServiceB

在io.binghe.spi.service.impl包下創(chuàng)建MyServiceB類,實(shí)現(xiàn)MyService接口。代碼如下所示:

package io.binghe.spi.service.impl;   import io.binghe.spi.service.MyService;   /**  * @author binghe  * @version 1.0.0  * @description 接口第二個(gè)實(shí)現(xiàn)  */ public class MyServiceB implements MyService {     @Override     public void print(String info) {         System.out.println(MyServiceB.class.getName() + " print " + info);     } }

6.創(chuàng)建接口文件

在項(xiàng)目的src/main/resources目錄下創(chuàng)建META/Services/目錄,在目錄中創(chuàng)建io.binghe.spi.service.MyService文件,注意:文件必須是接口MyService的全名,之后將實(shí)現(xiàn)MyService接口的類配置到文件中,如下所示:

io.binghe.spi.service.impl.MyServiceA io.binghe.spi.service.impl.MyServiceB

7.創(chuàng)建測(cè)試類

在項(xiàng)目的io.binghe.spi.main包下創(chuàng)建Main類,該類為測(cè)試程序的入口類,提供一個(gè)main()方法,在main()方法中調(diào)用ServiceLoader類加載MyService接口的實(shí)現(xiàn)類。并通過Java8的Stream將結(jié)果打印出來,如下所示:

package io.binghe.spi.main;   import io.binghe.spi.loader.MyServiceLoader; import io.binghe.spi.service.MyService;   import java.util.ServiceLoader; import java.util.stream.StreamSupport;   /**  * @author binghe  * @version 1.0.0  * @description 測(cè)試的main方法  */ public class Main {       public static void main(String[] args){         ServiceLoader loader = MyServiceLoader.loadAll(MyService.class);         StreamSupport.stream(loader.spliterator(), false).forEach(s -> s.print("Hello World"));     } }

8.測(cè)試實(shí)例

運(yùn)行Main類中的main()方法,打印出的信息如下所示:

io.binghe.spi.service.impl.MyServiceA print Hello World io.binghe.spi.service.impl.MyServiceB print Hello World  Process finished with exit code 0

通過打印信息可以看出,通過Java SPI機(jī)制正確加載出接口的實(shí)現(xiàn)類,并調(diào)用接口的實(shí)現(xiàn)方法。

源碼解析

這里,主要是對(duì)SPI的加載流程涉及到的java.util.ServiceLoader的源碼的解析。

進(jìn)入java.util.ServiceLoader的源碼,可以看到ServiceLoader類實(shí)現(xiàn)了java.lang.Iterable接口,如下所示。

public final class ServiceLoader  implements Iterable

說明ServiceLoader類是可以遍歷迭代的。

java.util.ServiceLoader類中定義了如下的成員變量:

// 加載具體實(shí)現(xiàn)類信息的前綴,也就是以接口命名的文件需要放到Jar包中的META-INF/services/目錄下 private static final String PREFIX = "META-INF/services/";  // 需要加載的接口 private final Class service;  // 類加載器,用于加載以接口命名的文件中配置的接口的實(shí)現(xiàn)類 private final ClassLoader loader;  // 創(chuàng)建ServiceLoader時(shí)采用的訪問控制上下文環(huán)境 private final AccessControlContext acc;  // 用來緩存已經(jīng)加載的接口實(shí)現(xiàn)類,其中,Key是接口實(shí)現(xiàn)類的完整類名,Value為實(shí)現(xiàn)類對(duì)象 private LinkedHashMap providers = new LinkedHashMap<>();  // 用于延遲加載實(shí)現(xiàn)類的迭代器 private LazyIterator lookupIterator;

可以看到ServiceLoader類中定義了加載前綴為“META-INF/services/”,所以,接口文件必須要在項(xiàng)目的src/main/resources目錄下的META-INF/services/目錄下創(chuàng)建。

從MyServiceLoader類調(diào)用ServiceLoader.load(clazz)方法進(jìn)入源碼,如下所示:

//根據(jù)類的Class對(duì)象加載指定的類,返回ServiceLoader對(duì)象 public static  ServiceLoader load(Class service) {  //獲取當(dāng)前線程的類加載器  ClassLoader cl = Thread.currentThread().getContextClassLoader();  //動(dòng)態(tài)加載指定的類,將類加載到ServiceLoader中  return ServiceLoader.load(service, cl); }

方法中調(diào)用了ServiceLoader.load(service, cl)方法,繼續(xù)跟蹤代碼,如下所示:

//通過ClassLoader加載指定類的Class,并將返回結(jié)果封裝到ServiceLoader對(duì)象中 public static  ServiceLoader load(Class service, ClassLoader loader){  return new ServiceLoader<>(service, loader); }

可以看到ServiceLoader.load(service,  cl)方法中,調(diào)用了ServiceLoader類的構(gòu)造方法,繼續(xù)跟進(jìn)代碼,如下所示:

//構(gòu)造ServiceLoader對(duì)象 private ServiceLoader(Class svc, ClassLoader cl) {  //如果傳入的Class對(duì)象為空,則判處空指針異常  service = Objects.requireNonNull(svc, "Service interface cannot be null");  //如果傳入的ClassLoader為空,則通過ClassLoader.getSystemClassLoader()獲取,否則直接使用傳入的ClassLoader  loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;  acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;  reload();

繼續(xù)跟reload()方法,如下所示。

//重新加載 public void reload() {  //清空保存加載的實(shí)現(xiàn)類的LinkedHashMap  providers.clear();  //構(gòu)造延遲加載的迭代器  lookupIterator = new LazyIterator(service, loader); }

繼續(xù)跟進(jìn)懶加載迭代器的構(gòu)造函數(shù),如下所示。

private LazyIterator(Class service, ClassLoader loader) {  this.service = service;  this.loader = loader; }

可以看到,會(huì)將需要加載的接口的Class對(duì)象和類加載器賦值給LazyIterator的成員變量。

當(dāng)我們?cè)诔绦蛑械@取對(duì)象實(shí)例時(shí),首先在成員變量providers中查找是否有緩存的實(shí)例對(duì)象。如果存在則直接返回,否則調(diào)用lookupIterator延遲加載迭代器進(jìn)行加載。

迭代器進(jìn)行邏輯判斷的代碼如下所示:

//迭代ServiceLoader的方法 public Iterator iterator() {  return new Iterator() {   //獲取保存實(shí)現(xiàn)類的LinkedHashMap的迭代器   Iterator> knownProviders = providers.entrySet().iterator();   //判斷是否有下一個(gè)元素   public boolean hasNext() {    //如果knownProviders存在元素,則直接返回true    if (knownProviders.hasNext())     return true;    //返回延遲加載器是否存在元素    return lookupIterator.hasNext();   }   //獲取下一個(gè)元素   public S next() {    //如果knownProviders存在元素,則直接獲取    if (knownProviders.hasNext())     return knownProviders.next().getValue();    //獲取延遲迭代器lookupIterator中的元素    return lookupIterator.next();   }    public void remove() {    throw new UnsupportedOperationException();   }  }; }

LazyIterator加載類的流程如下代碼所示

//判斷是否擁有下一個(gè)實(shí)例 private boolean hasNextService() {  //如果擁有下一個(gè)實(shí)例,直接返回true  if (nextName != null) {   return true;  }  //如果實(shí)現(xiàn)類的全名為null  if (configs == null) {   try {    //獲取全文件名,文件相對(duì)路徑+文件名稱(包名+接口名)    String fullName = PREFIX + service.getName();    //類加載器為空,則通過ClassLoader.getSystemResources()方法獲取    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()) {   //如果configs中沒有更過的元素,則直接返回false   if (!configs.hasMoreElements()) {    return false;   }   //解析包結(jié)構(gòu)   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 {   //加載類對(duì)象   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 {   //通過c.newInstance()生成對(duì)象實(shí)例   S p = service.cast(c.newInstance());   //將生成的對(duì)象實(shí)例保存到緩存中(LinkedHashMap)   providers.put(cn, p);   return p;  } catch (Throwable x) {   fail(service,     "Provider " + cn + " could not be instantiated",     x);  }  throw new Error();          // This cannot happen }  public boolean hasNext() {  if (acc == null) {   return hasNextService();  } else {   PrivilegedAction action = new PrivilegedAction() {    public Boolean run() { return hasNextService(); }   };   return AccessController.doPrivileged(action, acc);  } }  public S next() {  if (acc == null) {   return nextService();  } else {   PrivilegedAction action = new PrivilegedAction() {    public S run() { return nextService(); }   };   return AccessController.doPrivileged(action, acc);  } }

最后,給出整個(gè)java.util.ServiceLoader的類,如下所示:

package java.util;  import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedAction;   public final class ServiceLoader  implements Iterable {     // 加載具體實(shí)現(xiàn)類信息的前綴,也就是以接口命名的文件需要放到Jar包中的META-INF/services/目錄下     private static final String PREFIX = "META-INF/services/";      // 需要加載的接口     private final Class service;          // 類加載器,用于加載以接口命名的文件中配置的接口的實(shí)現(xiàn)類     private final ClassLoader loader;          // 創(chuàng)建ServiceLoader時(shí)采用的訪問控制上下文環(huán)境     private final AccessControlContext acc;          // 用來緩存已經(jīng)加載的接口實(shí)現(xiàn)類,其中,Key是接口實(shí)現(xiàn)類的完整類名,Value為實(shí)現(xiàn)類對(duì)象     private LinkedHashMap providers = new LinkedHashMap<>();          // 用于延遲加載實(shí)現(xiàn)類的迭代器     private LazyIterator lookupIterator;          //重新加載     public void reload() {         //清空保存加載的實(shí)現(xiàn)類的LinkedHashMap         providers.clear();         //構(gòu)造延遲加載的迭代器         lookupIterator = new LazyIterator(service, loader);     }          //構(gòu)造ServiceLoader對(duì)象     private ServiceLoader(Class svc, ClassLoader cl) {         //如果傳入的Class對(duì)象為空,則判處空指針異常         service = Objects.requireNonNull(svc, "Service interface cannot be null");         //如果傳入的ClassLoader為空,則通過ClassLoader.getSystemClassLoader()獲取,否則直接使用傳入的ClassLoader         loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;         acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;         reload();     }          private static void fail(Class service, String msg, Throwable cause)         throws ServiceConfigurationError     {         throw new ServiceConfigurationError(service.getName() + ": " + msg,                                             cause);     }          private static void fail(Class service, String msg)         throws ServiceConfigurationError     {         throw new ServiceConfigurationError(service.getName() + ": " + msg);     }          private static void fail(Class service, URL u, int line, String msg)         throws ServiceConfigurationError     {         fail(service, u + ":" + line + ": " + msg);     }          // Parse a single line from the given configuration file, adding the name     // on the line to the names list.     //     private int parseLine(Class service, URL u, BufferedReader r, int lc,                           List names)         throws IOException, ServiceConfigurationError     {         String ln = r.readLine();         if (ln == null) {             return -1;         }         int ci = ln.indexOf('#');         if (ci >= 0) ln = ln.substring(0, ci);         ln = ln.trim();         int n = ln.length();         if (n != 0) {             if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))                 fail(service, u, lc, "Illegal configuration-file syntax");             int cp = ln.codePointAt(0);             if (!Character.isJavaIdentifierStart(cp))                 fail(service, u, lc, "Illegal provider-class name: " + ln);             for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {                 cp = ln.codePointAt(i);                 if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))                     fail(service, u, lc, "Illegal provider-class name: " + ln);             }             if (!providers.containsKey(ln) && !names.contains(ln))                 names.add(ln);         }         return lc + 1;     }          private Iterator parse(Class service, URL u)         throws ServiceConfigurationError     {         InputStream in = null;         BufferedReader r = null;         ArrayList names = new ArrayList<>();         try {             in = u.openStream();             r = new BufferedReader(new InputStreamReader(in, "utf-8"));             int lc = 1;             while ((lc = parseLine(service, u, r, lc, names)) >= 0);         } catch (IOException x) {             fail(service, "Error reading configuration file", x);         } finally {             try {                 if (r != null) r.close();                 if (in != null) in.close();             } catch (IOException y) {                 fail(service, "Error closing configuration file", y);             }         }         return names.iterator();     }          // Private inner class implementing fully-lazy provider lookupload     private class LazyIterator         implements Iterator     {              Class service;         ClassLoader loader;         Enumeration configs = null;         Iterator pending = null;         String nextName = null;              private LazyIterator(Class service, ClassLoader loader) {             this.service = service;             this.loader = loader;         }              //判斷是否擁有下一個(gè)實(shí)例         private boolean hasNextService() {             //如果擁有下一個(gè)實(shí)例,直接返回true             if (nextName != null) {                 return true;             }             //如果實(shí)現(xiàn)類的全名為null             if (configs == null) {                 try {                     //獲取全文件名,文件相對(duì)路徑+文件名稱(包名+接口名)                     String fullName = PREFIX + service.getName();                     //類加載器為空,則通過ClassLoader.getSystemResources()方法獲取                     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()) {                 //如果configs中沒有更過的元素,則直接返回false                 if (!configs.hasMoreElements()) {                     return false;                 }                 //解析包結(jié)構(gòu)                 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 {                 //加載類對(duì)象                 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 {                 //通過c.newInstance()生成對(duì)象實(shí)例                 S p = service.cast(c.newInstance());                 //將生成的對(duì)象實(shí)例保存到緩存中(LinkedHashMap)                 providers.put(cn, p);                 return p;             } catch (Throwable x) {                 fail(service,                      "Provider " + cn + " could not be instantiated",                      x);             }             throw new Error();          // This cannot happen         }              public boolean hasNext() {             if (acc == null) {                 return hasNextService();             } else {                 PrivilegedAction action = new PrivilegedAction() {                     public Boolean run() { return hasNextService(); }                 };                 return AccessController.doPrivileged(action, acc);             }         }              public S next() {             if (acc == null) {                 return nextService();             } else {                 PrivilegedAction action = new PrivilegedAction() {                     public S run() { return nextService(); }                 };                 return AccessController.doPrivileged(action, acc);             }         }              public void remove() {             throw new UnsupportedOperationException();         }          }          //迭代ServiceLoader的方法     public Iterator iterator() {         return new Iterator() {             //獲取保存實(shí)現(xiàn)類的LinkedHashMap的迭代器             Iterator> knownProviders = providers.entrySet().iterator();             //判斷是否有下一個(gè)元素             public boolean hasNext() {                 //如果knownProviders存在元素,則直接返回true                 if (knownProviders.hasNext())                     return true;                 //返回延遲加載器是否存在元素                 return lookupIterator.hasNext();             }             //獲取下一個(gè)元素             public S next() {                 //如果knownProviders存在元素,則直接獲取                 if (knownProviders.hasNext())                     return knownProviders.next().getValue();                 //獲取延遲迭代器lookupIterator中的元素                 return lookupIterator.next();             }                  public void remove() {                 throw new UnsupportedOperationException();             }              };     }          //通過ClassLoader加載指定類的Class,并將返回結(jié)果封裝到ServiceLoader對(duì)象中     public static  ServiceLoader load(Class service,                                             ClassLoader loader)     {         return new ServiceLoader<>(service, loader);     }          //根據(jù)類的Class對(duì)象加載指定的類,返回ServiceLoader對(duì)象     public static  ServiceLoader load(Class service) {         //獲取當(dāng)前線程的類加載器         ClassLoader cl = Thread.currentThread().getContextClassLoader();         //動(dòng)態(tài)加載指定的類,將類加載到ServiceLoader中         return ServiceLoader.load(service, cl);     }          public static  ServiceLoader loadInstalled(Class service) {         ClassLoader cl = ClassLoader.getSystemClassLoader();         ClassLoader prev = null;         while (cl != null) {             prev = cl;             cl = cl.getParent();         }         return ServiceLoader.load(service, prev);     }          /**      * Returns a string describing this service.      *      * @return  A descriptive string      */     public String toString() {         return "java.util.ServiceLoader[" + service.getName() + "]";     } }

SPI總結(jié)

最后,對(duì)Java提供的SPI機(jī)制進(jìn)行簡(jiǎn)單的總結(jié)。

優(yōu)點(diǎn):

能夠?qū)崿F(xiàn)項(xiàng)目解耦,使得第三方服務(wù)模塊的裝配控制的邏輯與調(diào)用者的業(yè)務(wù)代碼分離,而不是耦合在一起。應(yīng)用程序可以根據(jù)實(shí)際業(yè)務(wù)情況啟用框架擴(kuò)展或替換框架組件。

缺點(diǎn):

  • 多個(gè)并發(fā)多線程使用ServiceLoader類的實(shí)例是不安全的

  • 雖然ServiceLoader也算是使用的延遲加載,但是基本只能通過遍歷全部獲取,也就是接口的實(shí)現(xiàn)類全部加載并實(shí)例化一遍。

“如何理解Java中的SPI機(jī)制”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!


文章題目:如何理解Java中的SPI機(jī)制
網(wǎng)站網(wǎng)址:http://weahome.cn/article/jjoecj.html

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部