@SpringBootApplication

@SpringBootApplication是一个3合一注解:

  1. //@SpringBootApplication
  2. @SpringBootConfiguration
  3. @EnableAutoConfiguration
  4. @ComponentScan("com.atguigu.boot")
  5. public class MainApplication {
  6. ...
  7. }

@SpringBootConfiguration

  1. //
  2. // Source code recreated from a .class file by IntelliJ IDEA
  3. // (powered by Fernflower decompiler)
  4. //
  5. package org.springframework.boot;
  6. import java.lang.annotation.Documented;
  7. import java.lang.annotation.ElementType;
  8. import java.lang.annotation.Retention;
  9. import java.lang.annotation.RetentionPolicy;
  10. import java.lang.annotation.Target;
  11. import org.springframework.context.annotation.Configuration;
  12. import org.springframework.core.annotation.AliasFor;
  13. @Target({ElementType.TYPE})
  14. @Retention(RetentionPolicy.RUNTIME)
  15. @Documented
  16. @Configuration
  17. public @interface SpringBootConfiguration {
  18. @AliasFor(
  19. annotation = Configuration.class
  20. )
  21. boolean proxyBeanMethods() default true;
  22. }

@Configuration。代表当前是一个配置类

@ComponentScan

指定扫描哪些包,Spring注解;

@EnableAutoConfiguration

  1. @AutoConfigurationPackage
  2. @Import(AutoConfigurationImportSelector.class)
  3. public @interface EnableAutoConfiguration {

导入主程序类所在的包及其子包@AutoConfigurationPackage

翻译过来叫自动配置包?指定了默认的包规则,看源码

  1. @Import(AutoConfigurationPackages.Registrar.class)
  2. public @interface AutoConfigurationPackage {

它导入了一个Registrar,这是个内部类,点进去看源码,debug调试
image.png
利用Registrar给容器中导入一系列组件,将指定的一个包com.atguigu.boot下的所有组件导入进来
默认是主程序类MainApplication 所在包下。
image.png

导入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

点进AutoConfigurationImportSelector去看源码selectImports方法

  1. @Override
  2. public String[] selectImports(AnnotationMetadata annotationMetadata) {
  3. if (!isEnabled(annotationMetadata)) {
  4. return NO_IMPORTS;
  5. }
  6. AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
  7. return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  8. }

getAutoConfigurationEntry方法

  1. protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
  2. if (!isEnabled(annotationMetadata)) {
  3. return EMPTY_ENTRY;
  4. }
  5. AnnotationAttributes attributes = getAttributes(annotationMetadata);
  6. List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  7. configurations = removeDuplicates(configurations);
  8. Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  9. checkExcludedClasses(configurations, exclusions);
  10. configurations.removeAll(exclusions);
  11. configurations = getConfigurationClassFilter().filter(configurations);
  12. fireAutoConfigurationImportEvents(configurations, exclusions);
  13. return new AutoConfigurationEntry(configurations, exclusions);
  14. }

getCandidateConfigurations方法

  1. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  2. List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
  3. getBeanClassLoader());
  4. Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
  5. + "are using a custom packaging, make sure that file is correct.");
  6. return configurations;
  7. }

loadFactoryNames方法

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

loadSpringFactories方法

  1. private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
  2. MultiValueMap<String, String> result = cache.get(classLoader);
  3. if (result != null) {
  4. return result;
  5. }
  6. try {
  7. Enumeration<URL> urls = (classLoader != null ?
  8. classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
  9. ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
  10. result = new LinkedMultiValueMap<>();
  11. while (urls.hasMoreElements()) {
  12. URL url = urls.nextElement();
  13. UrlResource resource = new UrlResource(url);
  14. Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  15. for (Map.Entry<?, ?> entry : properties.entrySet()) {
  16. String factoryTypeName = ((String) entry.getKey()).trim();
  17. for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
  18. result.add(factoryTypeName, factoryImplementationName.trim());
  19. }
  20. }
  21. }
  22. cache.put(classLoader, result);
  23. return result;
  24. }
  25. catch (IOException ex) {
  26. throw new IllegalArgumentException("Unable to load factories from location [" +
  27. FACTORIES_RESOURCE_LOCATION + "]", ex);
  28. }
  29. }

加载了一个配置文件叫“META-INF/spring.factories”
image.png
在这一步拿到所有的配置,返回上层我们看一共有130个
image.png
默认扫描我们当前系统里面所有spring.factories位置的文件,spring.factories位于spring-boot-autoconfigure-2.3.4.RELEASE.jar包当中META-INF/spring.factories,spring.factories文件里面写死了spring-boot一启动就要给容器中加载的所有配置类
image.png
spring.factories,这里当然也包括我们熟悉的伙伴spring-boot-starter-web的自动配置类包
image.png
总结以下,SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration

  • 每个自动配置类是个配置类@Configuration
  • 每个自动配置类按照条件进行生效@ConditionalOnXXXX
  • 生效的配置类就会给容器中装配很多组件@Bean,容器放了这些组件,相当于这些功能就有了
  • 更激动人心的是,自动配置类会为我们绑定参数配置文件,类注解@EnableConfigurationProperties(ServerProperties.class)

    有趣的自动配置MultipartResolver

    image.png
    源码解读:

    1. @Bean
    2. //容器中有这个类型组件
    3. @ConditionalOnBean(MultipartResolver.class)
    4. //容器中没有这个名字 multipartResolver 的组件
    5. @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
    6. //给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
    7. public MultipartResolver multipartResolver(MultipartResolver resolver) {
    8. //SpringMVC multipartResolver。防止有些用户配置的文件上传解析器名字不叫multipartResolver,不符合规范
    9. // Detect if the user has created a MultipartResolver but named it incorrectly
    10. return resolver;
    11. }
    12. 给容器中加入了文件上传解析器;并且返回的resolvermultipartResolver,因为我们的bean方法名叫multipartResolver
    13. 相当于强制的给用户配置的视图解析器重命名为multipartResolver

    配置文件参数默认映射

    @EnableConfigurationProperties

    @EnableConfigurationProperties(ServerProperties.class)标注在类上
    image.png

    @ConfigurationProperties

    @ConfigurationProperties(prefix = “server”)标注在参数类上
    所以用户还可以去看这个组件是获取的yaml/properties配置文件什么值,prefix是什么,就去修改

    1. @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
    2. public class ServerProperties {
    3. /**
    4. * Server HTTP port.
    5. */
    6. private Integer port;
    7. /**
    8. * Network address to which the server should bind.
    9. */
    10. private InetAddress address;
    11. @NestedConfigurationProperty
    12. private final ErrorProperties error = new ErrorProperties();
    13. ...
    14. }

    image.png

    允许用户可以定制化组件

    用户可以直接自己@Bean替换底层Spring默认的组件

    有趣的组件CharacterEncodingFilter

image.png
底层的ConditionalOnMissingBean意味着Spring的一种设计模式:条件装载,当此Bean,CharacterEncodingFilter不存在时才生效,意味着会向容器当中放置一个Spring默认的CharacterEncodingFilter。SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先

  1. @Bean
  2. @ConditionalOnMissingBean
  3. public CharacterEncodingFilter characterEncodingFilter() {
  4. CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
  5. filter.setEncoding(this.properties.getCharset().name());
  6. filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
  7. filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
  8. return filter;
  9. }