spring boot读取properties文件spring.factories–分享牛

在学习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);。

非常的简单。

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


Original url: Access
Created at: 2019-03-04 16:18:46
Category: default
Tags: none

请先后发表评论
  • 最新评论
  • 总共0条评论