ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【SpringBoot】2、运行原理初探【狂神篇】

2022-01-17 19:33:02  阅读:109  来源: 互联网

标签:SpringBoot spring 配置 boot 狂神篇 public 初探 class


1、pom.xml

自已建的 Spring Boot 工程,默认就有一个父工程 spring-boot-starter-parent,父工程里面还有一个父工程 spring-boot-dependencies

  • 自建工程
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.2</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
  • 父工程 spring-boot-starter-parent
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.6.2</version>
</parent>
  • 父工程 spring-boot-dependencies

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

以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;

2、启动器

spring-boot-starter

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

spring-boot-starter-xxx:就是spring-boot的场景启动器

spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件;

SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ;我们未来也可以自己自定义 starter;

3、主启动类

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

1)@SpringBootApplication

表明这是一个 Spring Boot 应用

标注在某个类上说明这个类是SpringBoot的主配置类 , SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;只能有一个

@SpringBootApplication 注解后面有三个重要注解

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(...)
public @interface SpringBootApplication {}

(1)@SpringBootConfiguration

SpringBoot配置:表示该类是SpringBoot的一个配置

@Configuration
public @interface SpringBootConfiguration {}

@Component
public @interface Configuration {}
  • @Configuration

    说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件

    • @Component

      启动类本身也是Spring中的一个组件而已,负责启动应用!

(2)@EnableAutoConfiguration

开启自动配置功能

以前需要自己配置的东西,而现在SpringBoot可以自动配置 ;

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

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}
  • @AutoConfigurationPackage

    自动配置包

    @Import({Registrar.class})
    public @interface AutoConfigurationPackage {}
    
    • @Import({Registrar.class})

      @import :Spring底层注解 @import , 给容器中导入一个组件

      Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器

  • @Import({AutoConfigurationImportSelector.class})

    给容器导入组件

    • AutoConfigurationImportSelector

      自动配置导入选择器

      • getCandidateConfigurations()

        获得候选的配置

        AutoConfigurationImportSelector 类中的方法

        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            // getSpringFactoriesLoaderFactoryClass()方法
            // 返回的就是最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
            List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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()

        SpringFactoriesLoader 类的静态方法

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

        private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
            // 获得classLoader,这里得到的就是EnableAutoConfiguration标注的类本身
            Map<String, List<String>> result = (Map)cache.get(classLoader);
            if (result != null) {
                return result;
            } else {
                HashMap result = new HashMap();
        
                try {
                    // 获取一个资源 "META-INF/spring.factories"
                    Enumeration urls = classLoader.getResources("META-INF/spring.factories");
        
                    while(urls.hasMoreElements()) {
                        URL url = (URL)urls.nextElement();
                        UrlResource resource = new UrlResource(url);
                        Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                        Iterator var6 = properties.entrySet().iterator();
        
                        // 将读取到的资源遍历,封装成为一个Properties
                        while(var6.hasNext()) {
                            Entry<?, ?> entry = (Entry)var6.next();
                            String factoryTypeName = ((String)entry.getKey()).trim();
                            String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                            String[] var10 = factoryImplementationNames;
                            int var11 = factoryImplementationNames.length;
        
                            for(int var12 = 0; var12 < var11; ++var12) {
                                String factoryImplementationName = var10[var12];
                                ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                                    return new ArrayList();
                                })).add(factoryImplementationName.trim());
                            }
                        }
                    }
        
                    result.replaceAll((factoryType, implementations) -> {
                        return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
                    });
                    cache.put(classLoader, result);
                    return result;
                } catch (IOException var14) {
                    throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
                }
            }
        }
        
  • spring.factories

    自动配置根源所在

    在这里插入图片描述

自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中对应的 org.springframework.boot.autoconfigure 包下的配置项,通过反射实例化为对应标注了 @Configuration 的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中

(3)@ComponentScan

组件扫描

扫描当前主启动类同级的包

结论:

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

    所有的自动配置类都在这里面,但不一定都生效;要判断条件是否成立,只有导入了对应的启动器starter,才会自动装配,才会生效

    判断条件的注解:

    @ConditionalOnXxx

    @ConditionalOnMissingXXX

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

  3. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;

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

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

2)run

run不是简单运行一个方法,而是启动一个服务

@SpringBootApplication
public class HelloworldApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloworldApplication.class, args);
    }
}
  • SpringApplication

    这个类主要做了以下四件事情:

    1、推断应用的类型是普通的项目还是Web项目

    2、查找并加载所有可用初始化器 , 设置到initializers属性中

    3、找出所有的应用程序监听器,设置到listeners属性中

    4、推断并设置main方法的定义类,找到运行的主类

    构造器

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.sources = new LinkedHashSet();
    this.bannerMode = Mode.CONSOLE;
    this.logStartupInfo = true;
    this.addCommandLineProperties = true;
    this.addConversionService = true;
    this.headless = true;
    this.registerShutdownHook = true;
    this.additionalProfiles = Collections.emptySet();
    this.isCustomEnvironment = false;
    this.lazyInitialization = false;
    this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
    this.applicationStartup = ApplicationStartup.DEFAULT;
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}
  • run方法流程分析

在这里插入图片描述

标签:SpringBoot,spring,配置,boot,狂神篇,public,初探,class
来源: https://blog.csdn.net/tu_wer/article/details/122546417

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有