Java&Android開發-淺析ServiceLoader類

image

1.SPI的概念

瞭解ServiceLoader,須要先了解 SPI(Service Provider Interface)java

SPI的簡單來講就是在程序設計時將一個功能服務的接口與實現分離,在程序運行時經過JVM機制自動找到服務接口的實現類並建立,以達到解耦的目的,提升程序的可拓展性; 好比JDBCgit

2.ServiceLoader

ServiceLoader就是 Java平臺提供的一個簡單的 Service Provder Framework。使用ServiceLoader有簡單的如下幾個步驟github

  • 建立服務接口
  • 在服務接口的實現模塊中,建立一個實現類實現對應的服務接口,並經過在項目的resource/META-INF/services文件夾下面建立一個對應該服務接口全限定名的文本文件,在該文本文件寫入該服務接口實現類的全限定名,以此達到一個註冊服務的做用(項目打包後在jar文件裏也得存在該文件)
  • 服務調用方(需求方)經過ServiceLoader類的load方法加載服務並獲得服務的實現類

2.1 一個簡單ServiceLoader場景實例

這裏以一個簡單虛擬支付場景爲例。 有一個業務模塊目前須要使用支付服務,因此咱們首先建立了一個PaymenService抽象接口表示 支付服務,接口類中有一個抽象方法**pay(String productName,double price)**表示支付某個商品 ###建立服務實現模塊緩存

package com.knight.serviceimpl;

import com.knight.PaymentService;

public class PaymentServiceImpl implements PaymentService {
    @Override
    public void pay(String productName, double price) {
        System.out.println("支付模塊:購買產品 "+productName +",價格"+price);
    }
}

複製代碼

在IDEA中的結構以下 app

image

建立服務接口類

image

經過ServiceLoader獲取服務

業務模塊中直接經過ServiceLoader類及PaymentService接口獲取服務實例並實現業務邏輯(業務模塊通常是不包含服務的實現模塊的)ide

package com.knight.business;

import com.knight.PaymentService;

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

public class BusinessModule {
    public static void run(){
        Iterator<PaymentService> serviceIterator = ServiceLoader.load(PaymentService.class).iterator();
        if (serviceIterator.hasNext()){
            PaymentService paymentService = serviceIterator.next();
            paymentService.pay("Q幣充值",100.00);
        }else {
            System.out.println("未找到支付模塊");
        }
    }
}

複製代碼

以上的核心代碼是 經過 ServiceLoader的load方法,傳入PaymentService接口類,會返回一個ServiceLoader的實例對象,經過該對象的**iterator()**方法會返回一個 Iterator 的迭代器,能夠經過這個迭代器獲得全部PaymentService的實現對象。gradle

最後 咱們再建立一個app模塊運行業務代碼邏輯,app模塊包含service、service-impl、business、3個模塊。ui

image

以上全部代碼已上傳 gitthis

ServiceLoader 核心源碼簡單解析

ServiceLoader內部細節 1.首先經過靜態方法load獲取對應服務接口的ServiceLoader實例; 2.ServiceLoader類繼承了Iterabale接口,內部實現了一個服務實例懶加載的迭代器;迭代器內部經過classLoader讀 取對應META-INF/service/文件夾下的服務配置文件獲取到全部的實現類類名稱,當經過iterator()方法獲取迭代器後,就能夠依次實例化service的實現並將實現對象加入到緩存中。 3.解析配置文件的過程就是按行讀取,每一行的文本都是一個服務實現類的全限定名,獲取到類名就能夠經過反射實例化對象了google

public final class ServiceLoader<S> implements Iterable<S> {
    //Service配置文件的資源路徑
    private static final String PREFIX = "META-INF/services/";

    // The class or interface representing the service being loaded
    private final Class<S> service;

    // 負責service配置資源加載,實例化service 
    private final ClassLoader loader;
    
        // 服務實例的緩存,已被迭代被建立過的會加到這個cache中
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
    
        private ServiceLoader(Class<S> svc, ClassLoader cl) {
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        reload();
    }
    //重載load, 刪除緩存並從新實例化iterator
        public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }
    
    //懶加載的service迭代器
        private class LazyIterator implements Iterator<S> {

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

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

        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
        }

        public boolean hasNext() {
            return hasNextService();
        }

        public S next() {
            return nextService();
        }


    }
    //按行讀取文件,每一行都是服務實現類的接口名
    private Iterator<String> parse(Class<?> service, URL u)throws ServiceConfigurationError {
            InputStream in = null;
            BufferedReader r = null;
            ArrayList<String> 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();
        }
}
複製代碼

Google autoService

以上 當註冊服務實現時若是須要手動建立文件並寫入服務實現類名稱 不免有些繁瑣,咱們可使用谷歌提供的 AutoService 庫簡化這一過程

使用方式

  1. gradle 引入autoService
dependencies {
    compileOnly 'com.google.auto.service:auto-service:1.0-rc2'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc2'
}
複製代碼
  1. 服務實現類上加上@AutoService註解,參數爲服務抽象類
package com.knight.serviceimpl;

import com.google.auto.service.AutoService;
import com.knight.PaymentService;

@AutoService(PaymentService.class)
public class PaymentServiceImpl implements PaymentService {
    @Override
    public void pay(String productName, double price) {
        System.out.println("支付模塊:購買產品 "+productName +",價格"+price);
    }
}

複製代碼

3.項目編譯觸發auto-service註解處理過程後自動生成了配置文件

image

其餘

該實例源碼已上傳 git倉庫

相關文章
相關標籤/搜索