• Spring Factories机制


    Spring Factories机制简述

    Spring Factories机制和Java SPI的扩展机制类似,Spring Boot采用了spring.factories的扩展机制,在很多spring的starter 包中都可以看到,通过读取 META-INF/spring.factories文件中的配置指定自动配置类入口,然后在程序中读取这些配置文件并实例化,从而让框架加载该类实现jar的动态加载。比如我们自定义的一些Spring Boot Starter公共组件就可以使用Spring Factories机制,通过极简的配置就可以在需要使用组件的地方引入依赖直接使用。

    什么是SPI机制

    SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。

    这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI机制。

    Spring Factories实现原理

    spring-core包(博主的是版本是5.2.8.RELEASE)里定义了SpringFactoriesLoader类,这个类实现了检索META-INF/spring.factories文件,并获取指定接口的配置的功能。

    以下为SpringFactoriesLoader类的注释说明:

    General purpose factory loading mechanism for internal use within the framework
    即该类是为框架内部使用工厂加载机制服务的。

    SpringFactoriesLoader loads and instantiates factories of a given type from FACTORIES_RESOURCE_LOCATION files which may be present in multiple JAR files in the classpath.
    SpringFactoriesLoader类通过读取spring.factories文档(该文档会存在多个classpath的多个jar包中),对给定的type及factoryClass加载和实例化其工厂类。

    The spring.factories file must be in java.util.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.factories格式:key为接口或抽象类全称,value为具体实现类全称的列表

    举例如下:

    # PropertySource Loaders
    org.springframework.boot.env.PropertySourceLoader=
    org.springframework.boot.env.PropertiesPropertySourceLoader,
    org.springframework.boot.env.YamlPropertySourceLoader
    
    # Run Listeners
    org.springframework.boot.SpringApplicationRunListener=
    org.springframework.boot.context.event.EventPublishingRunListener
    

    通过FACTORIES_RESOURCE_LOCATION指定扫描的配置文件路径:

    /**
     * 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";
    

    类中定义了两个公共的方法:

    • loadFactories 根据接口类获取其实现类的实例,这个方法返回的是对象列表。
    • loadFactoryNames 根据接口获取其接口类的名称,这个方法返回的是类名的列表。

    其中还包含了私有方法:

    • instantiateFactory 根据类名创建实例对象。
    • loadSpringFactories 通过扫描配置文件获取接口名称和接口实现类名称列表。

    loadFactories

    loadFactories方法首先调用loadFactoryNames方法获取待实例化的具体实现类的全称,然后调用instantiateFactory方法实例化每一个具体实现类,最终返回一个具体实现类的实例列表。

    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();
    	}
    	//调用loadFactoryNames获取接口的实现类
    	List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
    	if (logger.isTraceEnabled()) {
    		logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
    	}
    	//遍历 factoryNames 数组,创建实现类的对象
    	List<T> result = new ArrayList<>(factoryImplementationNames.size());
    	for (String factoryImplementationName : factoryImplementationNames) {
    		//调用instantiateFactory根据类创建实例对象
    		result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
    	}
    	//排序
    	AnnotationAwareOrderComparator.sort(result);
    	return result;
    }
    

    instantiateFactory

    根据实现类名实例化每一个具体实现类返回

    @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);
    	}
    }
    

    loadFactoryNames

    在这个方法中会遍历整个ClassLoader中所有jar包下的spring.factories文件。通过Properties解析所有接口的实现类名称。

    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    	String factoryTypeName = factoryType.getName();
    	return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    

    loadSpringFactories

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    	MultiValueMap<String, String> result = cache.get(classLoader);
    	if (result != null) {
    		return result;
    	}
    
    	try {
    		Enumeration<URL> urls = (classLoader != null ?
    				classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
    				ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    		result = new LinkedMultiValueMap<>();
    		while (urls.hasMoreElements()) {
    			URL url = urls.nextElement();
    			UrlResource resource = new UrlResource(url);
    			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
    			for (Map.Entry<?, ?> entry : properties.entrySet()) {
    				String factoryTypeName = ((String) entry.getKey()).trim();
    				//StringUtils.commaDelimitedListToStringArray:将配置文件中的value(接口实现类名称)通过","分隔成String数组
    				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);
    	}
    }
    
  • 相关阅读:
    pandas Dataframe filter
    process xlsx with pandas
    data manipulate in excel with easyExcel class
    modify registry in user environment
    add number line in vim
    java import webservice
    ctypes MessageBoxA
    music 163 lyrics
    【python实例】自动贩卖机
    【python基础】sys模块(库)方法汇总
  • 原文地址:https://www.cnblogs.com/curtinliu/p/14178342.html
Copyright © 2020-2023  润新知