• SpringBoot运行原理


    目录

    一、父依赖

    二、启动器(spring-boot-starter)

    三、主启动类

    @SpringBootApplication源码 

    @SpringBootConfiguration(源码)

    @EnableAutoConfiguration源码

    ⚪核心配置文件 META-INF/spring.factories

    ⭐总结 

    四、主启动类的运行

    1.SpringApplication 实例化

    2.run方法的执行

    五、配置文件

    1.分析自动配置原理 

    2.总结

    六、 @Conditional


    一、父依赖

    打开新建的项目的pom.xml:

    我们会发现它主要依赖于一个父项目,管理项目的资源过滤及插件

    点进去,发现还有一个父依赖   

    点击进入,这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心     

    1. "1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    3. <modelVersion>4.0.0modelVersion>
    4. <groupId>org.springframework.bootgroupId>
    5. <artifactId>spring-boot-dependenciesartifactId>
    6. <version>2.7.3version>
    7. <packaging>pompackaging>
    8. <name>spring-boot-dependenciesname>
    9. <description>Spring Boot Dependenciesdescription>
    10. <url>https://spring.io/projects/spring-booturl>
    11. <licenses>
    12. <license>
    13. <name>Apache License, Version 2.0name>
    14. <url>https://www.apache.org/licenses/LICENSE-2.0url>
    15. license>
    16. licenses>
    17. <developers>
    18. <developer>
    19. <name>Pivotalname>
    20. <email>info@pivotal.ioemail>
    21. <organization>Pivotal Software, Inc.organization>
    22. <organizationUrl>https://www.spring.ioorganizationUrl>
    23. developer>
    24. developers>
    25. <scm>
    26. <url>https://github.com/spring-projects/spring-booturl>
    27. scm>

    因此,我们在写或者引入一些SpringBoot依赖的时候,不需要指定版本(因为有这些版本仓库)

    二、启动器(spring-boot-starter)

    1. <dependency>
    2. <groupId>org.springframework.bootgroupId>
    3. <artifactId>spring-boot-starter-webartifactId>
    4. dependency>
    1. <dependencies>
    2. <dependency>
    3. <groupId>org.springframework.bootgroupId>
    4. <artifactId>spring-boot-starter-webartifactId>
    5. dependency>
    6. <dependency>
    7. <groupId>org.springframework.bootgroupId>
    8. <artifactId>spring-boot-starter-testartifactId>
    9. <scope>testscope>
    10. dependency>
    11. dependencies>
    • spring-boot-starter-web:帮助自动导入web环境所有的依赖
    • SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来
    • 也可以自己自定义 starter;

    三、主启动类

    1. //标注该类是一个SpringBoot的应用:启动类下的所有资源被导入
    2. @SpringBootApplication
    3. public class HelloworldApplication {
    4. public static void main(String[] args) {
    5. //将SpringBoot应用启动
    6. SpringApplication.run(HelloworldApplication.class, args);
    7. }
    8. }

    @SpringBootApplication源码 

    1. //
    2. // Source code recreated from a .class file by IntelliJ IDEA
    3. // (powered by FernFlower decompiler)
    4. //
    5. @Target({ElementType.TYPE})
    6. @Retention(RetentionPolicy.RUNTIME)
    7. @Documented
    8. @Inherited
    9. @SpringBootConfiguration
    10. @EnableAutoConfiguration
    11. //@ComponentScan:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中
    12. @ComponentScan(
    13. excludeFilters = {@Filter(
    14. type = FilterType.CUSTOM,
    15. classes = {TypeExcludeFilter.class}
    16. ), @Filter(
    17. type = FilterType.CUSTOM,
    18. classes = {AutoConfigurationExcludeFilter.class}
    19. )}
    20. )
    21. public @interface SpringBootApplication {
    22. @AliasFor(
    23. annotation = EnableAutoConfiguration.class
    24. )
    25. Class[] exclude() default {};
    26. @AliasFor(
    27. annotation = EnableAutoConfiguration.class
    28. )
    29. String[] excludeName() default {};
    30. @AliasFor(
    31. annotation = ComponentScan.class,
    32. attribute = "basePackages"
    33. )
    34. String[] scanBasePackages() default {};
    35. @AliasFor(
    36. annotation = ComponentScan.class,
    37. attribute = "basePackageClasses"
    38. )
    39. Class[] scanBasePackageClasses() default {};
    40. @AliasFor(
    41. annotation = ComponentScan.class,
    42. attribute = "nameGenerator"
    43. )
    44. Classextends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
    45. @AliasFor(
    46. annotation = Configuration.class
    47. )
    48. boolean proxyBeanMethods() default true;
    49. }

    @SpringBootConfiguration(源码)

    springboot的配置

    1. @Target({ElementType.TYPE})
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Configuration //spring配置类
    5. @Indexed
    6. public @interface SpringBootConfiguration {
    7. @AliasFor(
    8. annotation = Configuration.class
    9. )
    10. boolean proxyBeanMethods() default true;
    11. }
    1. @Target({ElementType.TYPE})
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Component //说明这也是一个spring组件
    5. public @interface Configuration {
    6. @AliasFor(
    7. annotation = Component.class
    8. )
    9. String value() default "";
    10. boolean proxyBeanMethods() default true;
    11. }


    @EnableAutoConfiguration源码

    @EnableAutoConfiguration:自动配置,告诉SpringBoot开启自动配置功能,这样自动配置才能生效;

    1. @Target({ElementType.TYPE})
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Inherited
    5. @AutoConfigurationPackage //自动配置包
    6. @Import({AutoConfigurationImportSelector.class}) //自动配置导入选择
    7. public @interface EnableAutoConfiguration {
    8. String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    9. Class[] exclude() default {};
    10. String[] excludeName() default {};
    11. }
    1. @Target({ElementType.TYPE})
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Inherited
    5. @Import({Registrar.class}) //自动配置包注册
    6. public @interface AutoConfigurationPackage {
    7. String[] basePackages() default {};
    8. Class[] basePackageClasses() default {};
    9. }
    1. static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    2. Registrar() {
    3. }
    4. public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    5. AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
    6. }
    7. public Set determineImports(AnnotationMetadata metadata) {
    8. return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
    9. }
    10. }
    11. ⚪核心配置文件 META-INF/spring.factories

      ⭐总结 

      springboot所有自动配置都是在启动的时候扫描并加载:spring.factories所有的自动配置类都在这里面,但是不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,自动装配就会生效,则配置成功

      1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值

      2. 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;

      3. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中,将所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器;

      4. 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;

      5. 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

      四、主启动类的运行

      1. //标注该类是一个SpringBoot的应用
      2. @SpringBootApplication
      3. public class HelloworldApplication {
      4. public static void main(String[] args) {
      5. //将SpringBoot应用启动
      6. //SpringApplication类
      7. //run方法
      8. SpringApplication.run(HelloworldApplication.class, args);
      9. }
      10. }

      1.SpringApplication 实例化

      • 推断应用的类型是普通的项目还是web项目
      • 查找并加载所有可用的初始化器,设置到initializers属性中
      • 找出所有的应用程序监听器,设置到initializers属性中
      • 推断并设置main方法的定义类,找到运行的主类

      构造器:

      1. public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
      2. this.sources = new LinkedHashSet();
      3. this.bannerMode = Mode.CONSOLE;
      4. this.logStartupInfo = true;
      5. this.addCommandLineProperties = true;
      6. this.addConversionService = true;
      7. this.headless = true;
      8. this.registerShutdownHook = true;
      9. this.additionalProfiles = Collections.emptySet();
      10. this.isCustomEnvironment = false;
      11. this.lazyInitialization = false;
      12. this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
      13. this.applicationStartup = ApplicationStartup.DEFAULT;
      14. this.resourceLoader = resourceLoader;
      15. Assert.notNull(primarySources, "PrimarySources must not be null");
      16. this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
      17. this.webApplicationType = WebApplicationType.deduceFromClasspath();
      18. this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
      19. this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
      20. this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
      21. this.mainApplicationClass = this.deduceMainApplicationClass();
      22. }

      2.run方法的执行

      1. public static ConfigurableApplicationContext run(Class primarySource, String... args) {
      2. return run(new Class[]{primarySource}, args);
      3. }
      4. public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) {
      5. return (new SpringApplication(primarySources)).run(args);
      6. }

      五、配置文件

      配置文件到底能写什么?怎么写???

      SpringBoot官方文档中有大量的配置,我们无法全部记住

      1.分析自动配置原理 

      ⚪以HttpEncodingAutoConfiguration(Http编码自动配置)为例

      1. //表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件;
      2. @Configuration
      3. //启动指定类的ConfigurationProperties功能;
      4. //进入这个HttpProperties查看,将配置文件中对应的值和HttpProperties绑定起来;
      5. //并把HttpProperties加入到ioc容器中
      6. @EnableConfigurationProperties({HttpProperties.class})
      7. //Spring底层@Conditional注解
      8. //根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效;
      9. //这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
      10. @ConditionalOnWebApplication(
      11. type = Type.SERVLET
      12. )
      13. //判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
      14. @ConditionalOnClass({CharacterEncodingFilter.class})
      15. //判断配置文件中是否存在某个配置:spring.http.encoding.enabled;
      16. //如果不存在,判断也是成立的
      17. //即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
      18. @ConditionalOnProperty(
      19. prefix = "spring.http.encoding",
      20. value = {"enabled"},
      21. matchIfMissing = true
      22. )
      23. public class HttpEncodingAutoConfiguration {
      24. //他已经和SpringBoot的配置文件映射了
      25. private final Encoding properties;
      26. //只有一个有参构造器的情况下,参数的值就会从容器中拿
      27. public HttpEncodingAutoConfiguration(HttpProperties properties) {
      28. this.properties = properties.getEncoding();
      29. }
      30. //给容器中添加一个组件,这个组件的某些值需要从properties中获取
      31. @Bean
      32. @ConditionalOnMissingBean //判断容器没有这个组件?
      33. public CharacterEncodingFilter characterEncodingFilter() {
      34. CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
      35. filter.setEncoding(this.properties.getCharset().name());
      36. filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
      37. filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
      38. return filter;
      39. }
      40. //。。。。。。。
      41. }

      2.总结

      根据当前不同的条件判断,决定这个配置类是否生效!

      六、 @Conditional

      @Conditional派生注解(Spring注解版原生的@Conditional作用)

      作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;

      那么多的自动配置类,必须在一定的条件下才能生效;也就是说,我们加载了这么多的配置类,但不是所有的都生效了。

      我们怎么知道哪些自动配置类生效?

      我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;

      Positive matches:(自动配置类启用的:正匹配)

      Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)

      Unconditional classes: (没有条件的类)

       

    12. 相关阅读:
      用go封装一下二级认证功能
      MySQL到底大小写敏感还是不敏感?
      程序员最爱用的在线代码编辑器合集,哪款是你的最爱?
      【BOOST C++ 线程】boost::thread库的基本使用方法总结
      Jetpack Compose 和 SwiftUI 与 Flutter 的比较
      概念解析 | 揭秘视觉与语言交叉模型:CLIP和BLIP的介绍
      SpringCloud 微服务注册中心 Eureka - Client
      Metabase学习教程:视图-8
      轻松实现视频、音频、文案批量合并,享受批量剪辑的便捷
      Docker常用应用部署
    13. 原文地址:https://blog.csdn.net/m0_52982868/article/details/126463756