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

【SpringBoot】2、运行原理初探【狂神篇】,第1张

【SpringBoot】2、运行原理初探【狂神篇】 1、pom.xml

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

自建工程


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

父工程 spring-boot-starter-parent


    org.springframework.boot
    spring-boot-dependencies
    2.6.2

父工程 spring-boot-dependencies

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

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

2、启动器

spring-boot-starter


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

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 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;
}

loadFactoryNames()

SpringFactoriesLoader 类的静态方法

public static List 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> loadSpringFactories(ClassLoader classLoader) {
    // 获得classLoader,这里得到的就是EnableAutoConfiguration标注的类本身
    Map> 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

组件扫描

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

结论:

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

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

    判断条件的注解:

    @ConditionalOnXxx

    @ConditionalOnMissingXXX

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

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

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

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

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方法流程分析

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5707466.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存