栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring Boot DAY02 运行原理的流程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring Boot DAY02 运行原理的流程

目录

1.pom.xml

1.1. 

1.2 .  

启动器 spring-boot-starter

2.主启动类

2.1 @SpringBootApplication

2.1.1 @ComponentScan

2.1.2 @SpringBootConfiguration

2.1.3 @EnableAutoConfiguration

2.1.4 spring.factories

2.2 SpringApplication


1.pom.xml

pom.xml文件中有两个关键的元素,一个是,另一个是

我们从这两个方面来分别说明pom.xml

1.1.

我们创建过maven项目,可以很明显的看出是一个父依赖,其中它其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件

 
        org.springframework.boot
        spring-boot-starter-parent
        2.6.2
         
    

然后点进org.springframework.boot,会发现其还有一个父依赖


    org.springframework.boot
    spring-boot-dependencies
    2.6.2
  

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

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

1.2 

初始化的中有两个元素,但都是基于启动器 spring-boot-starter的。

启动器 spring-boot-starter

    org.springframework.boot
    spring-boot-starter-web

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

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

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

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

2.主启动类

在项目建好时,会给我们一个默认的主启动类,当然我们也可以把它删了然后自己写

默认的主启动类:

package com.example.springboot;

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

@SpringBootApplication
public class SpringbootApplication {

    public static void main(String[] args) {

        SpringApplication.run(SpringbootApplication.class, args);
    }

}

我们可以从两个方面来分析主启动类,一个是注解@SpringBootApplication,另一个是SpringApplication中的run方法。

2.1 @SpringBootApplication

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

点进这个注解(截了一小部分):

@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}
)}
)

可以看出里面还有很多的其他注解

2.1.1 @ComponentScan

这个注解在Spring中很重要 ,它对应XML配置中的元素。

作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中

2.1.2 @SpringBootConfiguration

作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;

点进去继续看:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

然后接着点configuration:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
@Component
public @interface Configuration {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";

    boolean proxyBeanMethods() default true;
}

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

里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用!

我们回到 SpringBootApplication 注解中继续看。

2.1.3 @EnableAutoConfiguration

@EnableAutoConfiguration :开启自动配置功能

以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效;

点进注解查看:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
@Inherited
@AutoConfigurationPackage
@import({AutoConfigurationimportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class[] exclude() default {};

    String[] excludeName() default {};
}

@AutoConfigurationPackage :自动配置包,点进去看

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
@Inherited
@import({Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class[] basePackageClasses() default {};
}

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

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

再回到上一步:

@import({AutoConfigurationimportSelector.class}) :给容器导入组件 ;

AutoConfigurationimportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:

(1)类中个这样的方法

// 获得候选的配置
protected List getCandidateConfigurations(Annotationmetadata metadata, AnnotationAttributes attributes) {
    //这里的getSpringFactoriesLoaderFactoryClass()方法
    //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
    List 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;
}

2.他又调用了SpringFactoriesLoader 类的静态方法,然后我们可以进入SpringFactoriesLoader类loadFactoryNames() 方法

public static List loadFactoryNames(Class factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    //这里它又调用了 loadSpringFactories 方法
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

3我们继续点击查看 loadSpringFactories 方法

private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
    //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
    MultiValueMap result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
            //去获取一个资源 "meta-INF/spring.factories"
            Enumeration urls = classLoader != null ? classLoader.getResources("meta-INF/spring.factories") : ClassLoader.getSystemResources("meta-INF/spring.factories");
            linkedMultiValueMap result = new linkedMultiValueMap();

            //将读取到的资源遍历,封装成为一个Properties
            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 factoryClassName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;

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

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

4、发现一个多次出现的文件:spring.factories,全局搜索它

2.1.4 spring.factories

我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在

 我们在上面的自动配置类随便找一个打开看看,比如 :WebMvcAutoConfiguration

 可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean,可以找一些自己认识的类,看着熟悉一下!

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

结论:

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

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

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

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

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

其实我也看的一知半解的,也就是照着视频能走个流程

2.2 SpringApplication

SpringApplication.run分析

分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行;

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

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

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

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

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

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    // ......
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.setInitializers(this.getSpringFactoriesInstances();
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

用了个狂神的run的方法流程分析图

ps:这一部分看的很懵逼,也就看懂了个执行的流程,具体是怎么走的、怎么操作的、为啥这样写,咱也不懂,但是我看视频说面试会问这部分的问题,到时候还是要再看的,现在就具体熟悉下流程就行了,视频其实也就讲了个流程,以后会估计还会讲的深吧。

看到个B站的评论基本概括了这一章:

面试官问你你可以这样说,springboot是通过main方法下的SpringApplication.run方法启动的,启动的时候他会调用refshContext方法,先刷新容器,然后根据解析注解或者解析配置文件的形式祖册bean,而它是通过启动类的SpringBootApplication注解进行开始解析的,他会根EnableAutoConfiguration开启自动化配置,里面有个核心方法importSelect选择性的导入,根据loadFanctoryNames根据classpash路径以MATA-INF/spring.factorces下面以什么什么EnableAutoConfiguration开头的key去加载里面所有对应的自动化配置,他并不是把这一百二十多个自动化配置全部导入,在他每个自动化配置里面都有条件判断注解,先判断是否引入相互的jar包,再判断容器是否有bean再进行注入到bean容器。

2021/12/23 16:31

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/678487.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号