SpringFactoriesLoader原理解析

这是我参与更文挑战的第一天,活动详情查看:更文挑战

前言

SpringFactoriesLoader工厂加载机制是Spring内部提供的一个约定俗成的加载方式,与java spi类似,只需要在模块的META-INF/spring.factories文件,这个Properties格式的文件中的key是接口、注解、或抽象类的全名,value是以逗号 “ , “ 分隔的实现类,使用SpringFactoriesLoader来实现相应的实现类注入Spirng容器中。

下面以SpirngBoot的自动配置@EnableAutoConfiguration(springboot 2.0.x版本)的实现来讲解SpringFactoriesLoader。
9a34f7b82b199c25e9dfbb484b6d115
可以知道,该配置已经是满足了SpringFactoriesLoader的要求,下面我们来看看是如何来触发其找到相应的类并加载的。
e0f9430c60ab1a9de460882966d571a
可以看到在SpringBootApplication中有@EnableAutoConfiguration注解;
fd682d54d3078478d3c2dd570cd02c0
可以看到该注解,@Import(AutoConfigurationImportSelector.class)该注解会将AutoConfigurationImportSelector实例化并注入容器中。
AutoConfigurationImportSelector中selectImports方法
ae0f152489bba324de7584991e68372

selectImports方法的核心代码

AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
				annotationMetadata);
复制代码

一起看一下getAutoConfigurationEntry方法:

9d5015a60578ad02470b500ae55c0a7

getAutoConfigurationEntry方法的核心代码

List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
复制代码

getCandidateConfigurations方法如下图:
e8b51d5acd0c5dc53472e01223bac43

可知,这里终于调用了SpringFactoriesLoader的方法:

List configurations =SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
复制代码

getSpringFactoriesLoaderFactoryClass()返回的是EnableAutoConfiguration.class;

SpringFactoriesLoader.loadFactoryNames()方法:
78fcf64d070a894ff0bfa8ac3edb4af

这里 FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”;

可知,这个在便利所用的jar包下的META-INF/spring.factories文件,并对相应的key值进行筛选,这里使用的key值为org.springframework.boot.autoconfigure.EnableAutoConfiguration。

这样,我们就能得到对应的一组@Configuration类,我们就可以通过反射实例化这些类然后注入到IOC容器中,最后容器里就有了一系列标注了@Configuration的JavaConfig形式的配置类。

例如Tomcat容器的加载,在本文开头的spring.factories配置文件中,在key为EnableAutoConfiguration中有如下配置类:

org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
复制代码

EmbeddedWebServerFactoryCustomizerAutoConfiguration类:


	/**
	 * Nested configuration if Tomcat is being used.
	 */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
	public static class TomcatWebServerFactoryCustomizerConfiguration {

		@Bean
		public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment,
				ServerProperties serverProperties) {
			return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
		}

	}

	/**
	 * Nested configuration if Jetty is being used.
	 */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Server.class, Loader.class, WebAppContext.class })
	public static class JettyWebServerFactoryCustomizerConfiguration {

		@Bean
		public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(Environment environment,
				ServerProperties serverProperties) {
			return new JettyWebServerFactoryCustomizer(environment, serverProperties);
		}

	}

	/**
	 * Nested configuration if Undertow is being used.
	 */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Undertow.class, SslClientAuthMode.class })
	public static class UndertowWebServerFactoryCustomizerConfiguration {

		@Bean
		public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(Environment environment,
				ServerProperties serverProperties) {
			return new UndertowWebServerFactoryCustomizer(environment, serverProperties);
		}

	}

	/**
	 * Nested configuration if Netty is being used.
	 */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(HttpServer.class)
	public static class NettyWebServerFactoryCustomizerConfiguration {

		@Bean
		public NettyWebServerFactoryCustomizer nettyWebServerFactoryCustomizer(Environment environment,
				ServerProperties serverProperties) {
			return new NettyWebServerFactoryCustomizer(environment, serverProperties);
		}

	}
复制代码

可以看到该配置类中是对springboot2.0中支持四种web容器,关于载入哪种是根据其是否依赖了相应的容器实现类(@ConditionalOnClass控制实现)。

在springboot中默认是使用Tomcat做为web容器(springboot2.0.x),因为
5b7e9d89da8076b283c02727e233bb7

该依赖默认会导入Tomcat的jar包。

7f78058f3ba56650eb110e208e65c7c

在springboot中开发了大量的spring-boot-starter的组件,组件中依赖了相应的实现包。

总结

使用SpringFactoriesLoader寻找jar包配置META-INF下的spring.fatories配置文件相应key的value类,然后通过spring的@Configuration对相应的bean进行有选择(@ConditionalOnClass)的实例化。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享