chapter8:SpringBoot启动配置原理

尚硅谷SpringBoot顶尖教程

1. 启动流程简介

SpringBoot应用从主启动类启动后的运行流程主要包含下面几个要点:

(1)准备环境

  • 执行ApplicationContextInitializer#initialize()方法;
  • 监听器SpringApplicationRunListener回调contextPrepared方法
  • 加载主配置类定义信息
  • 监听器SpringApplicationRunListener回调contextLoaded方法;

(2)刷新启动IOC容器

  • 扫描加载所有容器中的组件;
  • 包括从META-INF/spring.factories文件中获取的所有EnableAutoConfiguration组件;

(3)回调容器中所有的ApplicationRunner,CommandLineRunner的run方法;

(4)监听器SpringApplicationRunListener回调finished方法

下面来看详细的启动流程分析, 课程讲解使用的是 1.5.10.RELEASE版本的SpringBoot源码。

2. 启动流程源码分析

2.1 创建SpringApplication对象

主启动类的main方法是Springboot应用启动的入口。

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

通过SpringApplication.run方法会进行SpringApplication对象的创建及部分初始化工作。

public class SpringApplication {
    // ...省略非核心代码
    // 主启动类入口调用的run方法
	public static ConfigurableApplicationContext run(Object source, String... args) {
		return run(new Object[] { source }, args);
	}
	
    // 继续走重载的run方法
	public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
		// 第一步:调用SpringApplication构造方法实例化对象
        // 第二步:创建SpringApplication对象后,继续调用重载的run方法
        return new SpringApplication(sources).run(args);
	}	

    // SpringApplication构造方法
	public SpringApplication(Object... sources) {
        // 初始化,参数是springboot应用的主启动类class对象
		initialize(sources);
	}
	
    // 初始化
	private void initialize(Object[] sources) {
        // 保存主配置类到this.sources属性中
		if (sources != null && sources.length > 0) {
			this.sources.addAll(Arrays.asList(sources));
		}
        // 判断当前springBoot应用是否是一个web应用,并保存判断结果到this.webEnvironment属性中
		this.webEnvironment = deduceWebEnvironment();
        // 从类路径下找到META-INF/spring.factories文件中配置的所有ApplicationContextInitializer并保存到this.initializers属性中。
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
        // 从类路径下找到META-INF/spring.factories文件中配置的所有ApplicationListener并保存到this.listeners属性中
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 从多个配置类中找到有main方法的主配置类,比如:SpringBoot02ConfigApplication
		this.mainApplicationClass = deduceMainApplicationClass();
	}
    
	// 判断当前springBoot应用是否是一个web应用 
    private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" };
    
	private boolean deduceWebEnvironment() {
		for (String className : WEB_ENVIRONMENT_CLASSES) {
            // 判断当前环境是否存在ConfigurableWebApplicationContext
			if (!ClassUtils.isPresent(className, null)) {
				return false;
			}
		}
		return true;
	}
	// 保存ApplicationContextInitializer到this.initializers属性中
	public void setInitializers(
			Collection<? extends ApplicationContextInitializer<?>> initializers) {
		this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
		this.initializers.addAll(initializers);
	}
	// 保存ApplicationListener到this.listeners属性中
	public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
		this.listeners = new ArrayList<ApplicationListener<?>>();
		this.listeners.addAll(listeners);
	}
	
	private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}

    // 从META-INF/spring.factories文件中查找ApplicationContextInitializer.class,ApplicationListener.class
	private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<String>(
            // META-INF/spring.factories 
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 实例化ApplicationContextInitializer,ApplicationListener
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
}
	// 从多个配置类中找到有main方法的主配置类,比如:SpringBoot02ConfigApplication
	private Class<?> deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
                // 通过当前的栈中找到有main方法的配置类,就是主启动类
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

spring-boot-1.5.10.RELEASE.jar!/META-INF/spring.factories文件配置了类的全限定名称;

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener

当然类路径下还有其他spring.factories文件都有可能配置的有要初始化的组件类名称,自定义spring.factories也可以。比如: 1.5.10.RELEASE\spring-boot-autoconfigure-1.5.10.RELEASE.jar!\META-INF\spring.factories中的初始化配置。

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

2.2 运行run方法

运行run方法继续完善IOC容器内容,源码分析如下:

public class SpringApplication {
    // ... 
	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
        // 配置IOC容器开始
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		FailureAnalyzers analyzers = null;
		configureHeadlessProperty();
        // 从类路径下/META-INF/spring.factories,获取SpringApplicationRunListener
		SpringApplicationRunListeners listeners = getRunListeners(args);
        // 调用所有的SpringApplicationRunListener#starting方法
		listeners.starting();
		try {
            // 封装命令行参数,主启动类main方法的args参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
            // 准备环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
            // 打印banner图标
			Banner printedBanner = printBanner(environment);
            // 创建IOC容器, 里面决定创建Web的IOC容器还是普通IOC容器。
			context = createApplicationContext();
			analyzers = new FailureAnalyzers(context);
            // 准备上下文环境,将environment保存到ioc容器中;
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
            // 刷新IOC容器及初始化
			refreshContext(context);
            // IOC容器初始化完成后
			afterRefresh(context, applicationArguments);
            // 所有的SpringApplicationRunListener回调finished()方法
			listeners.finished(context, null);
            // 配置IOC容器结束
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
            //整个SpringBoot应用启动完成后返回启动的IOC容器
			return context;
		}
		catch (Throwable ex) {
			handleRunFailure(context, listeners, analyzers, ex);
			throw new IllegalStateException(ex);
		}
	}
    
    // 从类路径下/META-INF/spring.factories,获取SpringApplicationRunListeners
	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
        // /META-INF/spring.factories
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}
    
    // type=SpringApplicationRunListener
    private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<String>(
            // /META-INF/spring.factories
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 创建SpringApplicationRunListener实例
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
        // 返回SpringApplicationRunListener实例
		return instances;
	}
    
    // 调用所有的SpringApplicationRunListener#starting方法
    public void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}
	
    // 准备环境
	private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
        // 创建环境完成后调用所有的SpringApplicationRunListener#environmentPrepared方法,表示环境准备完成。
		listeners.environmentPrepared(environment);
		if (!this.webEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		return environment;
	}
	//调用所有的SpringApplicationRunListener#environmentPrepared方法,表示环境准备完成。
	public void environmentPrepared(ConfigurableEnvironment environment) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.environmentPrepared(environment);
		}
	}
	
    // 嵌入式WEB环境的IOC容器
    public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
			+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
    // 普通IOC容器
    public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
			+ "annotation.AnnotationConfigApplicationContext";
    // 创建IOC容器	
    protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			// 创建web的IOC容器,还是普通的IOC容器
			contextClass = Class.forName(this.webEnvironment
						? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
		}
        // 返回实例化的IOC容器
		return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
	}
    // 准备上下文环境,将environment保存到ioc容器中;
	private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
        // 将environment保存到ioc容器中
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
        // 回调之前保存的所有ApplicationContextInitializer#initialize(context)方法
		applyInitializers(context);
        // 回调之前保存的所有SpringApplicationRunListener#contextPrepared(context)方法
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// Add boot specific singleton beans
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// Load the sources
		Set<Object> sources = getSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[sources.size()]));
        // 回调所有的SpringApplicationRunListener#contextLoaded(context)方法
		listeners.contextLoaded(context);
	}
	// 回调之前保存的所有的ApplicationContextInitializer#initialize(context)方法
	protected void applyInitializers(ConfigurableApplicationContext context) {
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
					initializer.getClass(), ApplicationContextInitializer.class);
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            // ApplicationContextInitializer#initialize(context)
			initializer.initialize(context);
		}
	}
	// 回调之前保存的所有SpringApplicationRunListener#contextPrepared(context)方法
	public void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
            // SpringApplicationRunListener#contextPrepared(context),表示IOC容器准备完成
			listener.contextPrepared(context);
		}
	}
	// 回调所有的SpringApplicationRunListener#contextLoaded(context)方法
	public void contextLoaded(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
            //SpringApplicationRunListener#contextLoaded(context), 表示IOC容器加载完成
			listener.contextLoaded(context);
		}
	}
    
    private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}
    //刷新IOC容器
    protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        //扫描、创建、加载IOC容器中的所有组件(配置类,组件,自动配置)。如果是web环境,还会创建嵌入式tomcat容器。
		((AbstractApplicationContext) applicationContext).refresh();
	}
	// IOC容器初始化完成后
	protected void afterRefresh(ConfigurableApplicationContext context,
			ApplicationArguments args) {
        //从IOC容器中获取所有的ApplicationRunner和CommandLineRunner进行回调。
		callRunners(context, args);
	}

	private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<Object>();
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet<Object>(runners)) {
			if (runner instanceof ApplicationRunner) {
                 //从IOC容器中获取所有的ApplicationRunner进行回调。
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
                 //从IOC容器中获取所有的CommandLineRunner进行回调。
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

	private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
		try {
            //回调ApplicationRunner#run
			(runner).run(args);
		}
		catch (Exception ex) {
			throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
		}
	}

	private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
		try {
            //回调CommandLineRunner#run
			(runner).run(args.getSourceArgs());
		}
		catch (Exception ex) {
			throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
		}
	}
	
	// 所有的SpringApplicationRunListener回调finished()方法
	public void finished(ConfigurableApplicationContext context, Throwable exception) {
		for (SpringApplicationRunListener listener : this.listeners) {
			callFinishedListener(listener, context, exception);
		}
	}

	private void callFinishedListener(SpringApplicationRunListener listener,
			ConfigurableApplicationContext context, Throwable exception) {
		// 回调SpringApplicationRunListener#finished
		listener.finished(context, exception);
	}
    // ...
}

我们来看运行run方法的debug过程:

从类路径下/META-INF/spring.factories,获取SpringApplicationRunListener
chapter8:SpringBoot启动配置原理_第1张图片
spring-boot-1.5.10.RELEASE.jar!\META-INF\spring.factories文件中的配置
chapter8:SpringBoot启动配置原理_第2张图片
debug调试结果:
chapter8:SpringBoot启动配置原理_第3张图片
回调所有的获取SpringApplicationRunListeners.starting方法
在这里插入图片描述
封装命令行参数:
在这里插入图片描述
准备创建环境,创建环境完成后回调SpringApplicationRunListener#environmentPrepared(environment)方法表示环境准备完成。
chapter8:SpringBoot启动配置原理_第4张图片
创建ApplicationContext容器,
chapter8:SpringBoot启动配置原理_第5张图片
里面决定创建Web的IOC容器还是普通IOC容器。
chapter8:SpringBoot启动配置原理_第6张图片
准备上下文环境,将environment保存到ioc容器中
chapter8:SpringBoot启动配置原理_第7张图片
applyInitializers(context) 回调之前保存的所有的ApplicationContextInitializer的initialize(context)方法
chapter8:SpringBoot启动配置原理_第8张图片
回调所有之前保存的SpringApplicationRunListener的contextPrepared(context)方法
chapter8:SpringBoot启动配置原理_第9张图片
prepareContext(…)方法运行完成后回调所有的SpringApplicationRunListener的contextLoaded(context)方法
chapter8:SpringBoot启动配置原理_第10张图片
刷新容器,IOC容器初始化,扫描、创建、加载IOC容器中的所有组件(配置类,组件,自动配置)。如果是web环境,还会创建嵌入式tomcat容器。
chapter8:SpringBoot启动配置原理_第11张图片
IOC容器初始化完成后,web应用(嵌入式tomcat)也已经启动完成。
chapter8:SpringBoot启动配置原理_第12张图片
从IOC容器中获取所有的ApplicationRunner和CommandLineRunner进行回调。ApplicationRunner先回调,CommandLineRunner后回调。
chapter8:SpringBoot启动配置原理_第13张图片
所有的SpringApplicationRunListener回调finished方法
chapter8:SpringBoot启动配置原理_第14张图片
整个SpringBoot应用启动完成后返回启动的IOC容器
chapter8:SpringBoot启动配置原理_第15张图片

2.3 自定义回调配置

主要看几个重要的事件回调机制 :

ApplicationContextInitializer, SpringApplicationRunListener配置在spring-boot-1.5.10.RELEASE.jar!/META-INF/spring.factories文件中;如果自定义xxxListener, 需要将自定义xxxListener或xxxInitializer放在当前应用类路径下的/META-INF/spring.factories文件中。

自定义ApplicationContextInitializer

public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
  @Override
  public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("HelloApplicationContextInitializer.initialize....." + configurableApplicationContext.getClass().getSimpleName());
    }
}

自定义SpringApplicationRunListener

public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {

    public HelloSpringApplicationRunListener(SpringApplication application, 
                                             String[] args) {
    }

    // 开始ioc配置
    @Override
    public void starting() {
        System.out.println("HelloSpringApplicationRunListener.starting.....");
    }

    // 准备环境
    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        Map<String, Object> systemProperties = environment.getSystemProperties();
        System.out.println("HelloSpringApplicationRunListener.environmentPrepared...," + systemProperties.get("os.name")
                + ',' + systemProperties.get("java.home"));
    }

    // ioc容器准备
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("HelloSpringApplicationRunListener.contextPrepared.....");
    }

    // ioc容器加载
    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("HelloSpringApplicationRunListener.contextLoaded.....");
    }

    // ioc容器配置完成
    @Override
    public void finished(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("HelloSpringApplicationRunListener.finished....");
    }
}

将自定义的ApplicationContextInitializer, SpringApplicationRunListener配置在当前项目类路径下的/META-INF/spring.factories文件中

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
com.atgugui.listener.HelloApplicationContextInitializer
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
com.atgugui.listener.HelloSpringApplicationRunListener

ApplicationRunner,CommandLineRunner组件只要放在IOC容器中就可以了;自定义xxxRunner,类上面加@Component即可.

/**
 * ApplicationRunner在IOC容器初始化过程中的SpringApplicationRunListeners#finished中执行
 */
@Component
public class HelloApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("HelloApplicationRunner.run.....");
    }
}

/**
 * CommandLineRunner在IOC容器初始化过程中的SpringApplicationRunListeners#finished中执行
 */
@Component
public class HelloCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("HelloCommandLineRunner.run..." + Arrays.asList(args));
    }
}

配置完成,启动应用,查看启动日志,和上面分析的源码执行顺序一样。

HelloSpringApplicationRunListener.starting 开始初始化
HelloSpringApplicationRunListener.environmentPrepared 准备环境

HelloApplicationContextInitializer.initialize方法初始化;

HelloSpringApplicationRunListener.contextPrepared方法准备上下文环境;

HelloSpringApplicationRunListener.contextLoaded方法加载上下文环境;

HelloSpringApplicationRunListener.finished方法上下文环境准备完成。
chapter8:SpringBoot启动配置原理_第16张图片

你可能感兴趣的:(spring,boot,spring,java)