zoukankan      html  css  js  c++  java
  • Spring boot主启动类探究

    注:原文来自狂神说公众号:https://mp.weixin.qq.com/s?__biz=Mzg2NTAzMTExNg==&mid=2247483743&idx=1&sn=431a5acfb0e5d6898d59c6a4cb6389e7&scene=19#wechat_redirect

    默认的主启动类

    package com.fan;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    // SpringBootApplication 来标注一个主程序类
    // 说明这是一个Spring boot应用
    @SpringBootApplication
    public class Springboot02ConfigApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(Springboot02ConfigApplication.class, args);
        }
    
    }
    

    接下来,分析注解。

    注解分析

    @SpringBootApplication

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

    进入这个注解:可以看到上面还有很多其他注解!

    @ComponentScan

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

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

    @SpringBootConfiguration

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

    我们继续进去这个注解查看:

    发现是@Configuration,再进:

    20200820012242

    发现是@Component。

    结论:

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

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

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

    @EnableAutoConfiguration

    20200820012601

    @AutoConfigurationPackage:自动配置包

    进入:

    20200820012923

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

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

    这个分析完了,退到上一步,继续看

    @Import(AutoConfigurationImportSelector.class):给容器导入组件;

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

    1、这个类中有一个这样的方法

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        //这里的getSpringFactoriesLoaderFactoryClass()方法
        //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                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<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        // 这里它又调用了 loadSpringFactories 方法
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    

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

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }
    
        try {
            //去获取一个资源 FACTORIES_RESOURCE_LOCATION--> "META-INF/spring.factories"
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
    
            //将读取到的资源遍历,封装成为一个Properties
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }
    

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

    spring.factories

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

    20200820014622

    结论

    1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
    2. 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
    3. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
    4. 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;
    5. 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

    SpringApplication

    不简单的方法

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

    SpringApplication.run分析

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

    SpringApplication

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

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

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

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

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

    查看构造器:

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }
    

    run方法流程图分析

    20200820015745

  • 相关阅读:
    选择排序
    冒泡排序
    排序介绍
    如何在服务器搭建JavaWeb项目环境(阿里轻量级)
    SSM整合配置文件
    如何删干净MySQL数据库
    spring概述
    Git简单命令
    第六天——读操作(二)
    第六天——文件操作(一)
  • 原文地址:https://www.cnblogs.com/fanlumaster/p/13532840.html
Copyright © 2011-2022 走看看