[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/ByteVortex/JavaGuide/main/docs/java/basis/spi.md [Back]  [Original]

---
title:  Java SPI 
category: Java
tag:
  - Java
head:
  - - meta
    - name: keywords
      content: Java SPI
  - - meta
    - name: description
      content: SPI  Service Provider Interface SPI 
---

>  [Kingshion](https://github.com/jjx0708)  JavaGuide [JavaGuide ](https://javaguide.cn/javaguide/contribution-guideline.html) 



Java SPI ** IoC **

## SPI 

###  SPI?

SPI  Service Provider Interface 

SPI 

 Java  SPI Spring  Dubbo 

![](https://oss.javaguide.cn/github/javaguide/java/basis/spi/22e1830e0b0e4115a882751f6c417857tplv-k3u1fbpfcp-zoom-1.jpeg)

### SPI  API 

** SPI  API **

 SPI  API 

![](https://oss.javaguide.cn/github/javaguide/java/basis/spi/1ebd1df862c34880bc26b9d494535b3dtplv-k3u1fbpfcp-watermark.png)



 API 

 SPI 

 H  H 

## 

SLF4J Simple Logging Facade for Java Java LogbackLog4jLog4j2  Maven  pom 

![](https://oss.javaguide.cn/github/javaguide/java/basis/spi/image-20220723213306039-165858318917813.png)

 SPI 

### Service Provider Interface

 Java  `service-provider-interface`  Java  Maven Maven  deploy 

```
  service-provider-interface.iml

.idea
    .gitignore
    misc.xml
    modules.xml
   workspace.xml

src
    edu
        jiangxuan
            up
                spi
                        Logger.java
                        LoggerService.java
                        Main.class
```

 `Logger`  SPI  

```java
package edu.jiangxuan.up.spi;

public interface Logger {
    void info(String msg);
    void debug(String msg);
}
```

 `LoggerService`  Java SPI 

```java
package edu.jiangxuan.up.spi;

import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;

public class LoggerService {
    private static final LoggerService SERVICE = new LoggerService();

    private final Logger logger;

    private final List loggerList;

    private LoggerService() {
        ServiceLoader loader = ServiceLoader.load(Logger.class);
        List list = new ArrayList();
        for (Logger log : loader) {
            list.add(log);
        }
        // LoggerList  ServiceProvider
        loggerList = list;
        if (!list.isEmpty()) {
            // Logger 
            logger = list.get(0);
        } else {
            logger = null;
        }
    }

    public static LoggerService getService() {
        return SERVICE;
    }

    public void info(String msg) {
        if (logger == null) {
            System.out.println("info  Logger ");
        } else {
            logger.info(msg);
        }
    }

    public void debug(String msg) {
        if (loggerList.isEmpty()) {
            System.out.println("debug  Logger ");
        }
        loggerList.forEach(log -> log.debug(msg));
    }
}
```

 `Main` 

```java
package org.spi.service;

public class Main {
    public static void main(String[] args) {
        LoggerService service = LoggerService.getService();

        service.info("Hello SPI");
        service.debug("Hello SPI");
    }
}
```



> info  Logger 
> debug  Logger 

 `Logger` 

 IDEA  jar 

### Service Provider

 `Logger` 

 `service-provider` 

```
  service-provider.iml

.idea
    .gitignore
    misc.xml
    modules.xml
   workspace.xml

lib
      service-provider-interface.jar
|
src
    edu
      jiangxuan
          up
              spi
                  service
                          Logback.java
    
    META-INF
        services
                edu.jiangxuan.up.spi.Logger

```

 `Logback` 

```java
package edu.jiangxuan.up.spi.service;

import edu.jiangxuan.up.spi.Logger;

public class Logback implements Logger {
    @Override
    public void info(String s) {
        System.out.println("Logback info " + s);
    }

    @Override
    public void debug(String s) {
        System.out.println("Logback debug " + s);
    }
}

```

 `service-provider-interface`  jar 

 lib  jar 

![](https://oss.javaguide.cn/github/javaguide/java/basis/spi/523d5e25198444d3b112baf68ce49daetplv-k3u1fbpfcp-watermark.png)

 OK 

![](https://oss.javaguide.cn/github/javaguide/java/basis/spi/f4ba0aa71e9b4d509b9159892a220850tplv-k3u1fbpfcp-watermark.png)

 jar  JDK 

 `Logger`  `src`  `META-INF/services`  `edu.jiangxuan.up.spi.Logger` SPI `edu.jiangxuan.up.spi.service.Logback` Logback  SPI  + 

** JDK SPI  ServiceLoader **

Java  SPI  class  `META-INF`  services  list 



 `service-provider`  jar  jar  maven  pom  jar  maven 

### 

`java-spi-test`

 `Logger`  jar  jar 

![](https://oss.javaguide.cn/github/javaguide/java/basis/spi/image-20220723215812708-165858469599214.png)

 Main 

```java
package edu.jiangxuan.up.service;

import edu.jiangxuan.up.spi.LoggerService;

public class TestJavaSPI {
    public static void main(String[] args) {
        LoggerService loggerService = LoggerService.getService();
        loggerService.info("");
        loggerService.debug("Java SPI ");
    }
}
```



> Logback info 
> Logback debug  Java SPI 

 jar 

 jar 

> info  Logger 
> debug  Logger 

 SPI `LoggerService`  `service-provider`  `Logger`  jar  SLF4J 

 Logback service-provider jar (LoggerService) (service-provider) 

 Java SPI  **ServiceLoader** 

## ServiceLoader

### ServiceLoader 

 Java  SPI  `ServiceLoader`  `ServiceLoader` 

`ServiceLoader`  JDK  `package java.util;`

```
A facility to load implementations of a service.
```

 JDK ****

 `final`  `Iterable` 

```java
public final class ServiceLoader implements Iterable{ xxx...}
```



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

 `load`  `load` 

```java
public static  ServiceLoader load(Class service) {
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    return ServiceLoader.load(service, cl);
}

public static  ServiceLoader load(Class service,
                                        ClassLoader loader) {
    return new ServiceLoader(service, loader);
}

private ServiceLoader(Class 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);
}
```

 `reload()`  `LazyIterator` 

`ServiceLoader`  `Iterable`  `iterator`  `ServiceLoader`  `Provider`  `LazyIterator` 

```java

public Iterator iterator() {
    return new Iterator() {

        Iterator knownProviders
                = providers.entrySet().iterator();

        public boolean hasNext() {
            if (knownProviders.hasNext())
                return true;
            return lookupIterator.hasNext(); //  LazyIterator
        }

        public S next() {
            if (knownProviders.hasNext())
                return knownProviders.next().getValue();
            return lookupIterator.next(); //  LazyIterator
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

    };
}
```

 `LazyIterator` 

```java

public boolean hasNext() {
    if (acc == null) {
        return hasNextService();
    } else {
        PrivilegedAction action = new PrivilegedAction() {
            public Boolean run() {
                return hasNextService();
            }
        };
        return AccessController.doPrivileged(action, acc);
    }
}

private boolean hasNextService() {
    if (nextName != null) {
        return true;
    }
    if (configs == null) {
        try {
            //PREFIXMETA-INF/services/ 
            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;
}


public S next() {
    if (acc == null) {
        return nextService();
    } else {
        PrivilegedAction action = new PrivilegedAction() {
            public S run() {
                return nextService();
            }
        };
        return AccessController.doPrivileged(action, acc);
    }
}

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
}
```

 `ServiceLoader` 

###  ServiceLoader



```java
package edu.jiangxuan.up.service;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

public class MyServiceLoader {

    //  Class 
    private final Class service;

    //   List 
    private final List providers = new ArrayList();

    // 
    private final ClassLoader classLoader;

    // 
    public static  MyServiceLoader load(Class service) {
        return new MyServiceLoader(service);
    }

    // 
    private MyServiceLoader(Class service) {
        this.service = service;
        this.classLoader = Thread.currentThread().getContextClassLoader();
        doLoad();
    }

    // 
    private void doLoad() {
        try {
            //  jar  META-INF/services 
            Enumeration urls = classLoader.getResources("META-INF/services/" + service.getName());
            // 
            while (urls.hasMoreElements()) {
                // 
                URL url = urls.nextElement();
                System.out.println("File = " + url.getPath());
                // 
                URLConnection urlConnection = url.openConnection();
                urlConnection.setUseCaches(false);
                // 
                InputStream inputStream = urlConnection.getInputStream();
                // 
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                // 
                String className = bufferedReader.readLine();

                while (className != null) {
                    // 
                    Class clazz = Class.forName(className, false, classLoader);
                    // Java
                    if (service.isAssignableFrom(clazz)) {
                        Constructor

Web Proxy Viewer  |  New URL  |  Original Page