• 解读Spring Boot启动过程之二:Web工程启动的主干流程


    main入口

    @SpringBootApplication
    public class MyApplication {
    
    	public static void main(String[] args) {
    		// 执行SpringAppllication的静态run方法
    		SpringApplication.run(MyApplication.class, args);
    
    	}
    
    }
    

    在Application的静态run方法内创建了SpringApplication实例,并调用了实例方法run

    
    	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    		// 首先,创建SpringApplication实例
    		return new SpringApplication(primarySources)
    				// 然后,调用其run方法
    				.run(args);
    	}
    

    SpringApplication的实例化

    	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    		// 资源加载器
    		this.resourceLoader = resourceLoader;
    		
    		Assert.notNull(primarySources, "PrimarySources must not be null");
    		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    		// 推断当前应用程序的类型
    		// 是Servlet? 还是Reactive?
    		this.webApplicationType = WebApplicationType.deduceFromClasspath();
    
    		// 从META-INF/spring.factories中加载BootstrapRegistryInitializer的实例
    		this.bootstrapRegistryInitializers = new ArrayList<>(
    				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    
    		// 从META-INF/spring.factories中加载ApplicationContextInitializer的实例
    		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    		
    		// 从META-INF/spring.factories中加载ApplicationListener的实例
    		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    		
    		// 推断main方法所在的类
    		this.mainApplicationClass = deduceMainApplicationClass();
    	}
    

    调用SpringApplication实例的run方法

    	public ConfigurableApplicationContext run(String... args) {
    		SpringApplicationHooks.hooks().preRun(this);
    		long startTime = System.nanoTime();
    		// 引导器 上下文
    		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    		
    		ConfigurableApplicationContext context = null;
    		
    		// 配置Headless属性
    		configureHeadlessProperty();
    		
    		// 从META-INF/spring.factories中加载SpringApplicationRunListener的实例,并封装到SpringApplicationRunListeners对象当中
    		SpringApplicationRunListeners listeners = getRunListeners(args);
    		
    		// 监听器广播事件:开始启动中
    		listeners.starting(bootstrapContext, this.mainApplicationClass);
    		try {
    			// 将运行参数 封装进ApplicationArguments中
    			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    			// 准备 Environment
    			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    			configureIgnoreBeanInfo(environment);
    			
    			// 打印Banner
    			Banner printedBanner = printBanner(environment);
    			
    			// 创建 应用上下文。若是Servlet应用,则返回AnnotationConfigSerlvetWebServerApplicationContext的实例。
    			context = createApplicationContext();
    			context.setApplicationStartup(this.applicationStartup);
    			
    			// 准备 应用上下文
    			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
    			if (refreshContext(context)) { // 刷新Context,若执行刷新成功,则返回true
    				// 在Context刷新后,默认是空方法
    				afterRefresh(context, applicationArguments);
    				
    				Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
    				if (this.logStartupInfo) {
    					new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(),
    							timeTakenToStartup);
    				}
    				// 广播事件:已启动
    				listeners.started(context, timeTakenToStartup);
    				
    				// 调用 Runner,包括CommandLineRunner和ApplicationRunner的实例
    				callRunners(context, applicationArguments);
    			}
    		}
    		catch (Throwable ex) {
    			// 处理异常
    			handleRunFailure(context, ex, listeners);
    			throw new IllegalStateException(ex);
    		}
    		try {
    			if (context.isRunning()) {
    				Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
    				listeners.ready(context, timeTakenToReady);
    			}
    		}
    		catch (Throwable ex) {
    			handleRunFailure(context, ex, null);
    			throw new IllegalStateException(ex);
    		}
    		SpringApplicationHooks.hooks().postRun(this, context);
    		return context;
    	}
    

    内置Tomcat(Jetty、undertow等)启动

    创建ApplicationContext对象。

    在run方法中,以下行创建了ApplicationContext对象。

    			// 创建 应用上下文。若是Servlet应用,则返回AnnotationConfigSerlvetWebServerApplicationContext的实例。
    			context = createApplicationContext();
    

    实际返回的 是 ApplicationContext的子类AnnotationConfigServletWebServerApplicationContext的实例

    调用ApplicationContext的refresh()

    创建AppliationContext之后,在run方法中,执行了refreshContext方法

    			if (refreshContext(context)) { // 刷新Context,若执行刷新成功,则返回true
    

    进入refreshContext,跟踪后,发现最终调用了ApplicationContext的refresh方法

    	protected void refresh(ConfigurableApplicationContext applicationContext) {
    		// 调用ApplicationContext的refresh方法
    		applicationContext.refresh();
    	}
    

    下面先看下 AnnotationConfigServletWebServerApplicationContext的继承结构
    image

    refresh方法是由其父类 AbstractApplicationContext实现的。
    下面的refresh()方法,重点关注方法中的 onRefresh()那一行

    	public void refresh() throws BeansException, IllegalStateException {
    		synchronized (this.startupShutdownMonitor) {
    			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
    
    			// 为刷新做一些准备工作
    			prepareRefresh();
    
    			// 获取一个新鲜的BeanFactory
    			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
    			// 为BeanFactory的使用做一些准备工作
    			prepareBeanFactory(beanFactory);
    
    			try {
    				// Allows post-processing of the bean factory in context subclasses.
    				// 留给子类,对BeanFactory作工作
    				postProcessBeanFactory(beanFactory);
    
    				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
    				// Invoke factory processors registered as beans in the context.
    				// 调用BeanFactoryPostProcessor
    				invokeBeanFactoryPostProcessors(beanFactory);
    
    				// Register bean processors that intercept bean creation.
    				registerBeanPostProcessors(beanFactory);
    				beanPostProcess.end();
    
    				// Initialize message source for this context.
    				initMessageSource();
    
    				// Initialize event multicaster for this context.
    				initApplicationEventMulticaster();
    
    				// Initialize other special beans in specific context subclasses.
    				// =========== 注意:为子类准备的方法,用于初始化特殊的bean
    				onRefresh();
    				// =============================================================
    				
    				// Check for listener beans and register them.
    				registerListeners();
    
    				// Instantiate all remaining (non-lazy-init) singletons.
    				finishBeanFactoryInitialization(beanFactory);
    
    				// Last step: publish corresponding event.
    				finishRefresh();
    			}
    
    			catch (BeansException ex) {
    				if (logger.isWarnEnabled()) {
    					logger.warn("Exception encountered during context initialization - " +
    							"cancelling refresh attempt: " + ex);
    				}
    
    				// Destroy already created singletons to avoid dangling resources.
    				destroyBeans();
    
    				// Reset 'active' flag.
    				cancelRefresh(ex);
    
    				// Propagate exception to caller.
    				throw ex;
    			}
    
    			finally {
    				// Reset common introspection caches in Spring's core, since we
    				// might not ever need metadata for singleton beans anymore...
    				resetCommonCaches();
    				contextRefresh.end();
    			}
    		}
    	}
    

    而AnnotationConfigServletWebServerApplicationContext的直接父类ServletWebServerApplicationContext重写了onRefresh方法

    调用onRefresh方法

    image

    createWebServer()

    可以看到,其中的createWebServer()便是用于创建及启动内嵌Tomcat的了~~

  • 相关阅读:
    git subtree用法
    Excel导入、导出库:ExcelKit
    [C#.NET 拾遗补漏]08:强大的LINQ
    使用.net standard实现不同内网端口的互通(类似花生壳)
    LINQ:最终统治了所有的语言!
    浅谈代码段加密原理(防止静态分析)
    HashTable、HashSet和Dictionary的区别(转)
    Mysql分表和分区的区别、分库分表介绍与区别
    划词高亮功能的实现附带开源代码
    十个推荐使用的 Laravel 的辅助函数
  • 原文地址:https://www.cnblogs.com/zhaojz/p/16382309.html
Copyright © 2020-2023  润新知