SpringBoot自动配置原理

引导类

package com.company;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BootDemoApplication {

public static void main(String[] args) {
SpringApplication.run(BootDemoApplication.class,args);
}
}

@SpringBootApplication注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration//启用自动配置
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

追run()方法

/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified source using default settings.
* @param primarySource the primary source to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}

  

继续追run()方法

/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified sources using default settings and user supplied arguments.
* @param primarySources the primary sources to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}

追构造方法

  

/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #SpringApplication(ResourceLoader, Class...)
* @see #setSources(Set)
*/
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}

继续

/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  //判断当前项目的应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}

追getSpringFactoriesInstances

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}

继续

  

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}

追loadFactoryNames

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

追loadSpringFactories

  

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
       //这里去加载自动配置文件,如果当前项目classpath中没有,则会去jar包中找。默认的自动配置文件在spring-boot-autoconfigure-2.2.2.RELEASE.jar!META-INFspring.factories
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();

while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();

while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;

for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}

cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}

spring.factories部分内容

  

# Initializers
org.springframework.context.ApplicationContextInitializer=
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=
org.springframework.boot.autoconfigure.condition.OnBeanCondition,
org.springframework.boot.autoconfigure.condition.OnClassCondition,
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
。。。

  键值对,值对应的都是org.springframework.boot.autoconfigure包中的内容,上图

  

在这个包下可以找到所有的默认配置

  比如org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

  

/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link EnableWebMvc Web MVC}.
*
* @author Phillip Webb
* @author Dave Syer
* @author Andy Wilkinson
* @author Sébastien Deleuze
* @author Eddú Meléndez
* @author Stephane Nicoll
* @author Kristine Jetzke
* @author Bruce Brouwer
* @author Artsiom Yudovin
* @since 2.0.0
*/
//有很多@Conditional开头的注解,表示在满足条件下才会生效
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)//满足类型web应用
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })//项目中存在这些类,引入了相关jar包就会有
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)//没有该类型的bean在容器中
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

找到InternalResourceViewResolver

  

@Bean
@ConditionalOnMissingBean//如果自定义配置了,就不会生效
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}

如果只想修改其中的属性值,一般也都只需要改变属性值即可,无需自己配置bean,追getter方法就可以找到WebMvcProperties.java

/**
* {@link ConfigurationProperties properties} for Spring MVC.
*
* @author Phillip Webb
* @author Sébastien Deleuze
* @author Stephane Nicoll
* @author Eddú Meléndez
* @author Brian Clozel
* @since 2.0.0
*/
@ConfigurationProperties(prefix = "spring.mvc")//这是一个配置类,前缀是spring.mvc
public class WebMvcProperties {

prefix和suffix是内部类View的属性

  

public static class View {

/**
* Spring MVC view prefix.
*/
private String prefix;

/**
* Spring MVC view suffix.
*/
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;
}

}

所以要覆盖的话,只需要在application.yaml中配置spring.mvc.view.prefix和suffix即可。其他想要覆盖的

spring-boot-autoconfigure-2.2.2.RELEASE.jar!META-INFadditional-spring-configuration-metadata.json和spring-configuration-metadata.json
中有英文解释,这两个文件用于idea在application.yaml文件中提示用,参考链接https://www.jianshu.com/p/4488ea10ab2f
spring:
mvc:
view:
prefix: /WEB-INF/pages
suffix: .html
原文地址:https://www.cnblogs.com/zou-rong/p/12564480.html