【JAVA SPI】Springboot 之 SpringFactoriesLoader

Spring Boot 的四大组件之一 auto-configuration,spring boot auto configuration加载相关的配置是通过spring framework Core提供的SpringFactoriesLoader工具类来实现的。

SpringFactoriesLoader loads and instantiates factories of a given type from "META-INF/spring.factories" files which may be present in multiple JAR files in the classpath. The spring.factories file must be in Properties format, where the key is the fully qualified name of the interface or abstract class, and the value is a comma-separated list of implementation class names. 

    上文是来自 spring 官方 spring core API中关于 SpringFactoriesLoader 的描述,信息点如下:

    1) SpringFactoriesLoader 会扫描 classpath 中的 META-INF/spring.factories文件。

    2) SpringFactoriesLoader 会加载并实例化 META-INF/spring.factories 中的制定类型

    3) META-INF/spring.factories 内容必须是 properties 的Key-Value形式,多值以逗号隔开。

    如果熟悉 java SPI,可以清晰的类比 SpringFactoriesLoader 是Spring 对 java SPI 的一种私有扩展,下面我们来看看 java SPI 是怎么通过ServiceLoader来实现的。

   

    1)在 src 目录下新建 META-INF/services目录,这俩个父子级目录的名称是不能变动的,原因ServiceLoader里写死了的,就是这么简单:

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

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

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

    // The class loader used to locate, load, and instantiate providers
    private final ClassLoader loader;

    // The access control context taken when the ServiceLoader is created
    private final AccessControlContext acc;

    // Cached providers, in instantiation order
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();

     2) 定义CPU的接口:

package com.simonton.api;

public interface CPU {
	
	public String info();
}

    3) 各厂商自己根据CPU接口做具体实现:

        a) AMD的实现:

package com.simonton;

import com.simonton.api.CPU;

public class AMD implements CPU{

	@Override
	public String info() {
		return "This is AMD";
	}

}

    b) Intel的实现:

import com.simonton.api.CPU;

public class Intel implements CPU {

	@Override
	public String info() {
		return "This is Intel CPU";
	}

}

4) 在使用时,根据需要,在META-INF/services/com.simonton.api.CPU文件中添加指定的实现。

5) 通过ServiceLoader来获取CPU的实现:

        ServiceLoader<CPU> cpuList = ServiceLoader.load(CPU.class);
		for (CPU cpu : cpuList) {
			System.out.println(cpu.info());
		}

    执行结果:

 

    ServiceLoader分析总结:ServiceLoader主要是扫描classpath的META-INF/services文件夹下,读取以接口包全名的文件内容,通过反射来加载及初始化指定类型的对象。其中关键的俩个方法如下:

        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);//获取Class对象
            } 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
        }

 

接下来我们来看看 Spring 的 SpringFactoriesLoader 和 ServiceLoader有何区别。

SpringFactoriesLoader 是spring core提供的一个工具类,源码很简洁,只有100来行代码:

public abstract class SpringFactoriesLoader {//为了避免工具类被实例化,定义成一个抽象类

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

	/**
	 * The location to look for factories.
	 * <p>Can be present in multiple JAR files.
	 */
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";//指定固定的文件路径


	/**
	 * Load and instantiate the factory implementations of the given type from
	 * {@value #FACTORIES_RESOURCE_LOCATION}, using the given class loader.
	 * <p>The returned factories are sorted in accordance with the {@link AnnotationAwareOrderComparator}.
	 * <p>If a custom instantiation strategy is required, use {@link #loadFactoryNames}
	 * to obtain all registered factory names.
	 * @param factoryClass the interface or abstract class representing the factory
	 * @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default)
	 * @see #loadFactoryNames
	 * @throws IllegalArgumentException if any factory implementation class cannot
	 * be loaded or if an error occurs while instantiating any factory
	 */
	public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
		Assert.notNull(factoryClass, "'factoryClass' must not be null");
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
		if (logger.isTraceEnabled()) {
			logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
		}
		List<T> result = new ArrayList<T>(factoryNames.size());
		for (String factoryName : factoryNames) {
			result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
		}
		AnnotationAwareOrderComparator.sort(result);
		return result;
	}

	/**
	 * Load the fully qualified class names of factory implementations of the
	 * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
	 * class loader.
	 * @param factoryClass the interface or abstract class representing the factory
	 * @param classLoader the ClassLoader to use for loading resources; can be
	 * {@code null} to use the default
	 * @see #loadFactories
	 * @throws IllegalArgumentException if an error occurs while loading factory names
	 */
	public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();//指定的类型全路径为Key
		try {
			Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			List<String> result = new ArrayList<String>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
				String factoryClassNames = properties.getProperty(factoryClassName);//获取指定的类型的实现类
				result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));//逗号拆分各种不同类型实现
			}
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
					"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

	@SuppressWarnings("unchecked")
	private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
		try {
			Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);//获取Class对象
			if (!factoryClass.isAssignableFrom(instanceClass)) {
				throw new IllegalArgumentException(
						"Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");
			}
			Constructor<?> constructor = instanceClass.getDeclaredConstructor();
			ReflectionUtils.makeAccessible(constructor);
			return (T) constructor.newInstance();//获取对象实例
		}
		catch (Throwable ex) {
			throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), ex);
		}
	}

}

    SpringFactoriesLoader 和 ServiceLoader 本质上说,原理都差不多:都是通过指定的规则,在规则定义的文件中配置接口的各种实现,通过key-value(properties formate)的方式读取,并以反射的方式实例化指定的类型。

转载于:https://my.oschina.net/simonton/blog/1791476

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值