在学习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);。
非常的简单。
欢迎关注微信公众号,您的肯定是对我最大的支持