盘古BPM体验地址    盘古BPM交流群盘古BPM交流群号:963222735

spring boot读取properties文件spring.factories

分享牛 23899℃

在学习spring boot内置的一系列监听器触发机制之前,咱们看一下spring boot是如何定义和加载这些监听器的,重点专注spring.factories文件中监听器的定义以及加载方式,Application Listeners。

spring.factories文件内容

首先,打开spring-boot\1.3.0.M1\spring-boot-1.3.0.M1\META-INF\spring.factories文件,该文件的内容如下所示:

# 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
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloudfoundry.VcapApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener

我们将关注点放置到上述代码中Application Listeners的定义其中,key为org.springframework.context.ApplicationListener。值比较多,一个九个,大家可以一个个的跟进代码进行查看。

spring.factories文件内容读取

我们新建一个测试方法如下所示:

public class SpringFactoriesLoaderTest {
public static void main(String[] args) throws IOException {
List<ApplicationListener> loadFactories = SpringFactoriesLoader.loadFactories(ApplicationListener.class, null);
System.out.println(loadFactories.size());
}
}

不出意外的话,程序输出的是9。

SpringFactoriesLoader.loadFactories源码跟踪

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

上述代码,首先获取类加载器,然后调用loadFactoryNames方法进行操作,然后开始遍历所有的factoryNames并调用instantiateFactory方法实例化这些类,最后调用AnnotationAwareOrderComparator.sort进行排序。

我们将关注点放置到loadFactoryNames方法的处理逻辑中。

loadFactoryNames方法

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		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);
		}
	}

上述代码中, FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";通过这里的处理我们可以看出来,默认读取的文件是META-INF/spring.factories。

那我们看一下PropertiesLoaderUtils.loadProperties方法。

注意一点:url中有文件流信息。

PropertiesLoaderUtils.loadProperties

public static Properties loadProperties(Resource resource) throws IOException {
		Properties props = new Properties();
		fillProperties(props, resource);
		return props;
	}

比较简单,首先实例化Properties 类,然后调用fillProperties方法进行处理,fillProperties方法如下:

public static void fillProperties(Properties props, Resource resource) throws IOException {
		InputStream is = resource.getInputStream();
		try {
			String filename = resource.getFilename();
			if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
				props.loadFromXML(is);
			}
			else {
				props.load(is);
			}
		}
		finally {
			is.close();
		}
	}

如果文件的后缀是.xml使用props.loadFromXML(is);方法,否则使用props.load(is);。

非常的简单。

欢迎关注微信公众号,您的肯定是对我最大的支持


转载请注明:分享牛 » spring boot读取properties文件spring.factories