• Spring Boot 启动时做了什么


    注解

    @SpringBootApplication注解 中包括三个注解:

    • @EnableAutoConfiguration:借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器
    • @Configuration:Spring Ioc容器的配置类,
    • @ComponentScan:组件扫描,可自动发现和装配Bean,功能其实就是自动扫描并加载符合条件的组件或者bean定义,最终将这些bean定义加载到IoC容器中.默认扫描SpringApplication的run方法里的class所在的包路径下文件,所以最好将该启动类放到根包路径下

    启动方法

    创建SpringApplication实例

        public SpringApplication(Class... primarySources) {
            this((ResourceLoader)null, primarySources);
        }
    
        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 = new HashSet();
            this.isCustomEnvironment = false;
            this.lazyInitialization = false;
            // 初始化资源加载器
            this.resourceLoader = resourceLoader;
            // 断言主要资源类不为空
            Assert.notNull(primarySources, "PrimarySources must not be null");
            // 初始化主要加载资源类集合
            this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
            // 判断项目类型
            this.webApplicationType = WebApplicationType.deduceFromClasspath();
            // 设置初始化器
            this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
            //设置监听器
            this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
            //推断程序主类
            this.mainApplicationClass = this.deduceMainApplicationClass();
        }
    

    初始化主要加载资源类集合

    判断项目类型

    	public enum WebApplicationType {
    		NONE,
    	    SERVLET,
    	    REACTIVE;
    	
    	    private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
    	    private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
    	    private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
    	    private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
    	    private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
    	    private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
    	
    	    private WebApplicationType() {
    	    }
    	
    	    static WebApplicationType deduceFromClasspath() {
    	        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
    	            return REACTIVE;
    	        } else {
    	            String[] var0 = SERVLET_INDICATOR_CLASSES;
    	            int var1 = var0.length;
    	
    	            for(int var2 = 0; var2 < var1; ++var2) {
    	                String className = var0[var2];
    	                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
    	                    return NONE;
    	                }
    	            }
    	
    	            return SERVLET;
    	        }
    	    }
    	}
    

    根据classpath中是否包含指定类
    1.REACTIVE:响应式WEB项目 ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)
    2.SERVLET:SERVLET WEB 项目 (!ClassUtils.isPresent("javax.servlet.Servlet", (ClassLoader)null)) && (!ClassUtils.isPresent("org.springframework.web.context.ConfigurableWebApplicationContext", (ClassLoader)null))
    3.NONE:非WEB项目

    设置应用上线文初始化器 ApplicationContextInitializer

    路径下META-INF/spring.factories文件ApplicationContextInitializer 接口的所有配置的类路径名称,用来初始化指定的 Spring 应用上下文,如注册属性资源、激活 Profiles 等。

    1. 获取类加载器
    2. 获取ApplicationContextInitializer实例名称
    3. 创建初始化器实例
    4. 给初始化器实例排序

    设置监听器 ApplicationListener

    路径下META-INF/spring.factories文件,使用初始化器相同的方法设置

    设置程序的主类

    执行run方法

    run方法流程

    创建计时器

    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    

    配置awt

    this.configureHeadlessProperty();
    
    private void configureHeadlessProperty() {
       System.setProperty("java.awt.headless", System.getProperty("java.awt.headless", Boolean.toString(this.headless)));
    }
    

    用来进行简单的图像处理,验证码生成等

    获取/启动SpringApplicationRunListeners

    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    listeners.starting();
    

    与初始化器方式一致,扫描META-INF/spring.factories 文件中实现类进行创建

    创建 ApplicationArguments

    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    

    创建/初始化ConfigurableEnvironment

    根据运行监听器和应用参数来准备 Spring 环境

    ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
    this.configureIgnoreBeanInfo(environment);
    
    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
    	// 创建应用环境 
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        // 配置应用环境
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        // TODO
        ConfigurationPropertySources.attach((Environment)environment);
        // 调用监听器的environmentPrepared方法
        listeners.environmentPrepared((ConfigurableEnvironment)environment);
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
        // TODO
        if (!this.isCustomEnvironment) {
            environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
        }
    
        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
    }
    

    根据创建SpringApplication 实例时判断的应用类型,创建实际的应用环境(Servlet 环境、响应式WEB环境和标准环境)

    private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
            return this.environment;
        } else {
            switch(this.webApplicationType) {
            case SERVLET:
                return new StandardServletEnvironment();
            case REACTIVE:
                return new StandardReactiveWebEnvironment();
            default:
                return new StandardEnvironment();
            }
        }
    }
    

    打印Banner

    Banner printedBanner = this.printBanner(environment);
    

    创建Banner,可以根据Banner原理客制化Banner。
    根据参数获取 spring.banner.image.location.
    根据参数获取 spring.banner.location banner.txt
    以上都没有,则打印SpringBootBanner
    因此最简单的方式则是在classpath下创建banner.txt文件
    可以自定义banner网站:https://www.bootschool.net/ascii

    创建应用上下文

    context = this.createApplicationContext();
    

    根据不同的应用类型创建不同的上下文

    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch(this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                    break;
                case REACTIVE:
                    contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                    break;
                default:
                    contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
                }
            } catch (ClassNotFoundException var3) {
                throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
            }
        }
    
        return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
    }
    

    准备异常报告器

    exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
    

    与初始化器方式一致,扫描META-INF/spring.factories 文件中实现类进行创建

    准备应用上下文

    this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    
    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    	// 设置上下文环境
        context.setEnvironment(environment);
        // 配置上下文bean生成器及加载器
        this.postProcessApplicationContext(context);
        // 执行初始化器
        this.applyInitializers(context);
        // 执行监听器的contextPrepared方法
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }
    
    	// 	注册单例bean
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
    
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
    
        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
    
    	// 加载资源
        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        this.load(context, sources.toArray(new Object[0]));
        // 触发监听器contextLoaded方法
        listeners.contextLoaded(context);
    }
    

    刷新应用上下文

    this.refreshContext(context);
    

    创建启动内置服务器

    private void refreshContext(ConfigurableApplicationContext context) {
        this.refresh((ApplicationContext)context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            } catch (AccessControlException var3) {
            }
        }
    
    }
    
    protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
        this.refresh((ConfigurableApplicationContext)applicationContext);
    }
    
    protected void refresh(ConfigurableApplicationContext applicationContext) {
        applicationContext.refresh();
    }
    

    刷新应用上下文后置

    this.afterRefresh(context, applicationArguments);
    

    目前此方法无实现,保留接口

    停止计时器

    stopWatch.stop();
    

    输出主类启动时间

    if (this.logStartupInfo) {
        (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
    }
    

    触发监听器started

    listeners.started(context);
    

    执行Runner

    this.callRunners(context, applicationArguments);
    

    分为ApplicationRunner和CommandRunner,会在服务启动时立即执行,可以在Runner中进行数据初始化等

    触发监听器running方法

    listeners.running(context);
    
  • 相关阅读:
    6种负载均衡算法
    Java中volatile关键字
    剑指offer练习
    linux系统查看IP地址,不显示IP地址或者只显示127.0.0.1
    Nginx负载均衡配置
    集群应用Session一致性实现的三种方案
    rabbitMQ学习
    JDK1.8在LINUX下安装步骤
    ecplise部署gradle web项目
    Kubernetes下的应用监控解决方案
  • 原文地址:https://www.cnblogs.com/herberts/p/13178161.html
Copyright © 2020-2023  润新知