@SpringBootApplication
@SpringBootApplication是一个3合一注解:
//@SpringBootApplication@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan("com.atguigu.boot")public class MainApplication {...}
@SpringBootConfiguration
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package org.springframework.boot;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.springframework.context.annotation.Configuration;import org.springframework.core.annotation.AliasFor;@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Configurationpublic @interface SpringBootConfiguration {@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;}
@ComponentScan
@EnableAutoConfiguration
@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration {
导入主程序类所在的包及其子包@AutoConfigurationPackage
翻译过来叫自动配置包?指定了默认的包规则,看源码
@Import(AutoConfigurationPackages.Registrar.class)public @interface AutoConfigurationPackage {
它导入了一个Registrar,这是个内部类,点进去看源码,debug调试
利用Registrar给容器中导入一系列组件,将指定的一个包com.atguigu.boot下的所有组件导入进来
默认是主程序类MainApplication 所在包下。
导入spring.factories当中的配置类@Import(AutoConfigurationImportSelector.class)
- 利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
- 调用List
configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类 130个 - 利用工厂加载 Map
> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件 - 从META-INF/spring.factories位置来加载一个文件。 默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件 ,META-INF/spring.factories,位于spring-boot-autoconfigure-2.3.4.RELEASE.jar包当中META-INF/spring.factories
- 利用工厂加载 Map
- 调用List
点进AutoConfigurationImportSelector去看源码selectImports方法
@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}
getAutoConfigurationEntry方法
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}AnnotationAttributes attributes = getAttributes(annotationMetadata);List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);configurations = removeDuplicates(configurations);Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = getConfigurationClassFilter().filter(configurations);fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);}
getCandidateConfigurations方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;}
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 = cache.get(classLoader);if (result != null) {return result;}try {Enumeration<URL> urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));result = new LinkedMultiValueMap<>();while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {result.add(factoryTypeName, factoryImplementationName.trim());}}}cache.put(classLoader, result);return result;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}}
加载了一个配置文件叫“META-INF/spring.factories”
在这一步拿到所有的配置,返回上层我们看一共有130个
默认扫描我们当前系统里面所有spring.factories位置的文件,spring.factories位于spring-boot-autoconfigure-2.3.4.RELEASE.jar包当中META-INF/spring.factories,spring.factories文件里面写死了spring-boot一启动就要给容器中加载的所有配置类
spring.factories,这里当然也包括我们熟悉的伙伴spring-boot-starter-web的自动配置类包
总结以下,SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
- 每个自动配置类是个配置类
@Configuration - 每个自动配置类按照条件进行生效
@ConditionalOnXXXX - 生效的配置类就会给容器中装配很多组件@Bean,容器放了这些组件,相当于这些功能就有了
更激动人心的是,自动配置类会为我们绑定参数配置文件,类注解
@EnableConfigurationProperties(ServerProperties.class)有趣的自动配置MultipartResolver

源码解读:@Bean//容器中有这个类型组件@ConditionalOnBean(MultipartResolver.class)//容器中没有这个名字 multipartResolver 的组件@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)//给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。public MultipartResolver multipartResolver(MultipartResolver resolver) {//SpringMVC multipartResolver。防止有些用户配置的文件上传解析器名字不叫multipartResolver,不符合规范// Detect if the user has created a MultipartResolver but named it incorrectlyreturn resolver;}给容器中加入了文件上传解析器;并且返回的resolver叫multipartResolver,因为我们的bean方法名叫multipartResolver相当于强制的给用户配置的视图解析器重命名为multipartResolver
配置文件参数默认映射
@EnableConfigurationProperties
@EnableConfigurationProperties(ServerProperties.class)标注在类上
@ConfigurationProperties
@ConfigurationProperties(prefix = “server”)标注在参数类上
所以用户还可以去看这个组件是获取的yaml/properties配置文件什么值,prefix是什么,就去修改@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)public class ServerProperties {/*** Server HTTP port.*/private Integer port;/*** Network address to which the server should bind.*/private InetAddress address;@NestedConfigurationPropertyprivate final ErrorProperties error = new ErrorProperties();...}
允许用户可以定制化组件
用户可以直接自己@Bean替换底层Spring默认的组件
有趣的组件CharacterEncodingFilter

底层的ConditionalOnMissingBean意味着Spring的一种设计模式:条件装载,当此Bean,CharacterEncodingFilter不存在时才生效,意味着会向容器当中放置一个Spring默认的CharacterEncodingFilter。SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先
@Bean@ConditionalOnMissingBeanpublic CharacterEncodingFilter characterEncodingFilter() {CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();filter.setEncoding(this.properties.getCharset().name());filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));return filter;}
