Spring Boot 在进行 SpringApplication 对象实例化时会加载 META-INF/spring.factories 文件,将该配置文件中的配置载入 Spring容器,进行自动配置。
1、首先进入启动 Spring Boot 项目代码 SpringApplication.run(App.class,args)的源码。
程序清单:org/springframework/boot/SpringApplication.java
public static ConfigurableApplicationContext run (Object[ ] sources,String[ ] args ){
return new SpringApplication( sources ).run( args ) ;
}
2、可以看到 run 方法实际上在创建 SpringApplication对象实例,下面来看创建 SpringApplication 对象实例的代码。
程序清单:org/springframework/boot/SpringApplication.java
public SpringApplication( Object... sources ){
initialize( sources ) ;
}
3、接下来就是调用 initialize( sources ) 方法,该方法的源码如下:
程序清单:org/springframework/boot/SpringApplication.java
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
initialize( sources ) 方法解析:
如果sources⻓度⼤于0的话,加⼊到SpringApplication的sources中,该sources是⼀个LinkedHashSet.
调⽤deduceWebEnvironment⽅法判断是否是web环境
设置initializers.
设置Listeners.
设置mainApplicationClass.
4、initialize 方法调用了 getSpringFactoriesInstances 方法,代码如下:
程序清单:org/springframework/boot/SpringApplication.java
private Collection extends T> getSpringFactoriesInstances(Class type,
Class>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
// 使⽤Set保存names来避免重复元素
Set names = new LinkedHashSet(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 根据names来进⾏实例化
List instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
// 对实例进⾏排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
getSpringFactoriesInstances 方法解析:
5、在 getSpringFactoriesInstances 中又调用了 loadFactoryNames 方法,继续进入该方法,查看源码如下:
程序清单:org/springframework/boot/SpringApplication.java
public static List loadFactoryNames(Class> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURC
E_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List result = new ArrayList();
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(factoryClassN
ames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
loadFactoryNames 方法解析:
6、从上述源码中可以看到架子啊了一个常量:FACTORIES_RESOURCE_LOCATION,该常量的源码如下:
/**
*The location to look for factories.
*Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION="META-INF/spring.factories";
从该源码中可以看出,最终 Spring Boot 是通过加载 META-INF/spring.factories 文件进行自动配置的。其所在位置如下图:
spring factories 文件非常重要,用来指导 Spring Boot 找到制定的自动配置文件。
spring factories 文件重点内容分析如下:
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
org.springframework.boot.env.PropertySourceLoader 对应的值表示指定的 Spring Boot 配置文件支持的格式。Spring Boot 的配置文件内置支持 properties、xml、yml、和 yaml 几种格式。其中 properties 和 xml 对应的 Loader 类为 PropertiesPropertySourceLoader,yml 和 yaml 对应的 Loader 类为 YamlPropertySourceLoader。
#Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
org.springframework.boot.SpringApplicationRunListener 对应的值表示运行的监听器类。默认会加载 EventPublishingRunListener,这个 RunListener 是在SpringApplication 对象的 run 方法执行到不同阶段时,发布相应的 event 给 SpringApplication 对象的 Listeners 中记录的事件监听器。
#Applicaiton Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer
org.springframework.context.ApplicationContextInitializer 对应的值表示 Spring Boot 中的应用程序的初始化类。默认加载4个ApplicationContextInitializer类。
在 spring.factories 中可以看出,Web 开发的自动配置类是 org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,这个类中自动实现了 Spring MVC 的配置。现在以 Spring MVC 的如下配置为例,了解 Spring Boot 是如何实现该自动配置的。
1、查询 WebMvcAutoConfiguration 的源码如下:
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration{
其中 @ConditionalOnClass 是一个条件注解,意思就是只有当前项目运行环境中有 Servlet 类,并且有 DispatcherServlet 类以及 WebMvcConfigurerAdapter 类(说明本项目是需要集成 Spring MVC 的),Spring Boot 才会初始化 WebMvcAutoConfiguration 进行自动配置。
2、自动配置视图解析器 ViewResolver:
在WebMvcAutoConfiguration 类下找到以下源码:
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}
@Bean 在这里定义了一个 Bean 对象 InternalResourceViewResolver,以前是通过
@ConditionalOnMissingBean 是一个条件注释,在当前环境下没有这个 Bean 的时候才会创建该 Bean。
方法在返回值即 InternalResourceViewResolver ,正是我们需要的对象,那么视图解析器中前缀和后缀 Spring Boot 是如何实现自动配置的呢?
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
该源码会找到一个 View对象:
public static class view{
/**
* Spring MVC 视图前缀
*/
private String prefix;
/**
* Spring MVC 视图后缀
*/
private String suffix;
public String getPrefix(){
return this.prefix;
}
public void setPrefix(String prefix){
this.prefix = prefix;
}
public String getSuffix(){
return this.suffix;
}
public void setSuffix(String suffix){
this.suffix = suffix;
}
}
View 对象则会通过获取prefix、suffix 加载视图解析器需要的前缀和后缀,该参数的值是可以通过全局配置文件来指定前缀和后缀的,配置如下:
spring.mvc.view.prefix = # Spring MVC view prefix
spring.mvc.view.suffix = # Spring MVC view suffix