在学习spring boot内置的一系列监听器触发机制之前,咱们看一下spring boot是如何定义和加载这些监听器的,重点专注spring.factories文件中监听器的定义以及加载方式,Application Listeners。
首先,打开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。值比较多,一个九个,大家可以一个个的跟进代码进行查看。
我们新建一个测试方法如下所示:
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。
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方法的处理逻辑中。
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中有文件流信息。
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
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
最新评论