ServiceLoader与SpringFactoriesLoader源码剖析

前言

SpringBoot中很多地方中使用了SPI扩展机制,那么它是什么实现的呢?本文将从JDK SPI源码开始讲起···

1. SPI扩展机制

SPI(Service Provider Interface),它的优势是接口与实现分离达到解耦、提升程序可扩展性的机制,可以做到轻松插拔。

1.1 JDK SPI

1.1.1 JDK SPI示例代码

public interface SpiProvider {
    void print();
}
public class SpiProvider1Impl implements SpiProvider {
    @Override
    public void print() {
        System.out.println("SpiProvider1Impl 11111111111111111");
    }
}
public class SpiProvider2Impl implements SpiProvider {
    @Override
    public void print() {
        System.out.println("SpiProvider2Impl 22222222222222222");
    }
}

在resources目录下创建META-INF/services/com.analyze.spi.api.SpiProvider文件,内容如下:

com.analyze.spi.api.impl.SpiProvider1Impl
com.analyze.spi.api.impl.SpiProvider2Impl
public class SpiTest {
    public static void main(String[] args) {
        ServiceLoader<SpiProvider> serviceLoader = ServiceLoader.load(SpiProvider.class);
        serviceLoader.forEach(SpiProvider::print);
    }
}

执行结果如下:

SpiProvider1Impl 11111111111111111
SpiProvider2Impl 22222222222222222

1.1.2 JDK SPI之ServiceLoader源码

ServiceLoader在rt.jar中。

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.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

public final class ServiceLoader<S> implements Iterable<S>{

	// 固定配置文件读取目录
    private static final String PREFIX = "META-INF/services/";

    // 抽象类或接口
    private final Class<S> service;

    // 用于加载、定位、实例化服务提供者
    private final ClassLoader loader;

    // 创建ServiceLoader时采取的访问控制上下文
    private final AccessControlContext acc;

    // 按实例化顺序缓存实现类或子类
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();

    // 延时迭代器
    private LazyIterator lookupIterator;

    /**
     * 使用当前线程为这个服务创建一个新的服务提供者加载器。
     * 
     * @param  <S> 	   服务提供者的类型
     * @param  service 服务提供者(接口或抽象类)
     *
     * @return 服务提供者加载器
     */
    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }
    
    /**
     * 为这个服务创建一个新的服务提供者加载器。
     *
     * @param  <S> 	   服务提供者的类型
     * @param  service 服务提供者(接口或抽象类)
     * @param  loader  服务提供者加载器,若为null则使用ClassLoader.getSystemClassLoader()创建
     *
     * @return 新的服务提供者加载器
     */
    public static <S> ServiceLoader<S> load(Class<S> service,
                                            ClassLoader loader)
    {
        return new ServiceLoader<>(service, loader);
    }
    
	/**
	 * 服务加载器构造
	 */
    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 LazyIterator(Class<S> service, ClassLoader loader) {
            this.service = service;
            this.loader = loader;
        }
        
		// 获取下一个实现类或子类
        public boolean hasNext() {
            if (acc == null) {
                return hasNextService();
            } else {
                PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                    public Boolean run() { return hasNextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

        private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                	// 获取配置文件路径 例如META-INF/services/com.analyze.spi.api.SpiProvider
                    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();
        }
        
        public S next() {
            if (acc == null) {
                return nextService();
            } else {
                PrivilegedAction<S> action = new PrivilegedAction<S>() {
                    public S run() { return nextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }
        
        public void remove() {
            throw new UnsupportedOperationException();
        }

    }
    /**
     * 解析配置文件中的一行,将该行中的名称添加到名称列表中。
     */
    private int parseLine(Class<?> service, URL u, BufferedReader r, int lc, List<String> 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;
    }

    /**
     * @param  service  服务提供者接口或抽象类
     * @param  u 		待解析的配置文件的URL
     * @return A 		配置文件中子类或实现类成员路径
     */
    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();
    }
    
    public Iterator<S> iterator() {
        return new Iterator<S>() {
        	// 获取对应实现类或子类缓存
            Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator();
            public boolean hasNext() {
            	// 判断是否从providers获取到缓存的实现类或子类信息
                if (knownProviders.hasNext())
                    return true;
                // 从迭代器中判断是否还存在下一个元素
                return lookupIterator.hasNext();
            }
            public S next() {
            	// 判断是否从providers获取到缓存的实现类或子类信息
                if (knownProviders.hasNext())
                    return knownProviders.next().getValue();
                // 从迭代器中获取下一个元素
                return lookupIterator.next();
            }
        };
    }
}

1.1.3 JDK SPI示例代码剖析

1)ServiceLoader.load()使用当前线程为服务提供者创建一个新的服务加载器。
2)使用forEach去循环调用对应接口实现时,首先判断是否获取到缓存,为否,则先判断是否存在下一个元素,存在则执行类的装载。
3)读取 META-INF/services下的接口或抽象类对应的配置文件。
4)获得所有能被实例化的类的全限定名,通过Class.forName() 载入类对象,并用 instance() 方法将类实例化。
6)把实例化后的类缓存到providers 对象中然后返回实例对象。

1.1.4 JDK SPI优缺点

优点:

  • 解耦,可插拔组件。

缺点:

  • 不能按需加载。有些场景下并不想用的实现类或子类,在获取的过程中也会遍历实例化一遍。
  • 非线程安全。

1.2 Spring SPI

1.2.1 Spring SPI示例代码

使用上面的接口与实现类,在resources目录下创建META-INF/spring.factories文件,内容如下:

com.analyze.spi.api.SpiProvider=\
com.analyze.spi.api.impl.SpiProvider1Impl,\
com.analyze.spi.api.impl.SpiProvider2Impl
public class SpiTest {
    public static void main(String[] args) {
        List<SpiProvider> serviceLoader = SpringFactoriesLoader.loadFactories(SpiProvider.class, null);
        serviceLoader.forEach(SpiProvider::print);
    }
}

执行结果如下:

SpiProvider1Impl 11111111111111111
SpiProvider2Impl 22222222222222222

1.2.2 Spring SPI之SpringFactoriesLoader源码

package org.springframework.core.io.support;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.UrlResource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

public final class SpringFactoriesLoader {

	/**
	 * 读取配置文件的位置
	 */
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

	private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);

	/**
	 * 缓存
	 * key:给定类型的加载器 value :(key:接口或抽象类全限定名 value:实现类或子类全限定名)
	 */
	private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();

	private SpringFactoriesLoader() {
	}

	/**
	 * 使用给定的类加载器加载并实例化‘META-INF/spring.factories’给定类型的实现类或子类。
	 * 通过AnnotationAwareOrderComparator进行排序。
	 * 如果需要自定义实例化的策略,可以通过loadFactoryNames()来获得所有注册工厂的名称。
	 * @param factoryType 工厂的接口或抽象类
	 * @param classLoader 加载器(为null则使用默认的SpringFactoriesLoader.class.getClassLoader())
	 */
	public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
		Assert.notNull(factoryType, "'factoryType' must not be null");
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		// 根据factoryType获取所有实现类或子类的全限定名列表,默认emptyList
		List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
		if (logger.isTraceEnabled()) {
			logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
		}
		List<T> result = new ArrayList<>(factoryImplementationNames.size());
		for (String factoryImplementationName : factoryImplementationNames) {
			result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
		}
		AnnotationAwareOrderComparator.sort(result);
		return result;
	}

	/**
	 * 获取给定类型的接口或抽象类的实现类或子类全限定名列表
	 * 
	 * @param factoryType 工厂的接口或抽象类
	 * @param classLoader 用于加载的类加载器(为null则使用默认的SpringFactoriesLoader.class.getClassLoader())
	 */
	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		// 载入并缓存所有jar下META-INF/spring.factories的配置,并根据工厂的接口或抽象类获取子类或实现类全限定名列表
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}

	/**
	 * 载入并缓存所有jar下META-INF/spring.factories的配置
	 */
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		// 从通过类加载器从缓存中获取
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}
		try {
			// 获取所有META-INF/spring.factories文件URL
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			// 遍历获取并存放在result中
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				// 加载Properties配置文件
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				// 解析Properties配置
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			// 遍历处理完毕后,放入到缓存,下次加载工厂时直接从缓存中获取
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}
	/**
	 * 通过反射实例化子类或实现类
	 * 
	 * @param factoryImplementationName 实现类或子类全限定名
	 * @param factoryType				接口或父类
	 * @param classLoader				类加载器
	 */
	@SuppressWarnings("unchecked")
	private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
		try {
			// 载入类对象
			Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
			if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
				throw new IllegalArgumentException(
						"Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
			}
			// 实例化该对象
			return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
		}
		catch (Throwable ex) {
			throw new IllegalArgumentException(
				"Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",
				ex);
		}
	}
}

1.2.3 Spring SPI示例代码剖析

1)载入并缓存所有jar下META-INF/spring.factories的配置。
2)根据工厂的接口或抽象类全限定名获取子类或实现类全限定名列表。
3)通过子类或实现类全限定名通过反射进行实例化。
4)通过AnnotationAwareOrderComparator进行排序。

结语

关于JDK SPI与Spring SPI就写到这,有什么疑问或问题欢迎在下面留言。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人生逆旅我亦行人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值