spring boot 2.0 源码分析(三)
通过上一章的源码分析,我们知道了spring boot里面的listeners到底是什么(META-INF/spring.factories定义的资源的实例),以及它是创建和启动的,今天我们继续深入分析一下SpringApplication实例变量中的run函数中的其他内容。还是先把run函数的代码贴出来:
/*** Run the Spring application, creating and refreshing a new* {@link ApplicationContext}.* @param args the application arguments (usually passed from a Java main method)* @return a running {@link ApplicationContext}*/public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);configureIgnoreBeanInfo(environment);Banner printedBanner = printBanner(environment);context = createApplicationContext();exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);prepareContext(context, environment, listeners, applicationArguments,printedBanner);refreshContext(context);afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}listeners.started(context);callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;}
在listeners启动了以后,我们来看一下ApplicationArguments applicationArguments
= new DefaultApplicationArguments(args); 在DefaultApplicationArguments的构造函数里,我们跟踪过去发现其最终调用的SimpleCommandLineArgsParser.parse函数:
public CommandLineArgs parse(String... args) {CommandLineArgs commandLineArgs = new CommandLineArgs();String[] var3 = args;int var4 = args.length;for(int var5 = 0; var5 < var4; ++var5) {String arg = var3[var5];if(arg.startsWith("--")) {String optionText = arg.substring(2, arg.length());String optionValue = null;String optionName;if(optionText.contains("=")) {optionName = optionText.substring(0, optionText.indexOf(61));optionValue = optionText.substring(optionText.indexOf(61) + 1, optionText.length());} else {optionName = optionText;}if(optionName.isEmpty() || optionValue != null && optionValue.isEmpty()) {throw new IllegalArgumentException("Invalid argument syntax: " + arg);}commandLineArgs.addOptionArg(optionName, optionValue);} else {commandLineArgs.addNonOptionArg(arg);}}return commandLineArgs;}
从这段代码中我们看到DefaultApplicationArguments其实是读取了命令行的参数。
小发现:通过分析这个函数的定义,你是不是想起了spring boot启动的时候,用命令行参数自定义端口号的情景?
java -jar MySpringBoot.jar --server.port=8000
接着往下看:ConfigurableEnvironment environment = this.prepareEnvironment(listeners, ex);
通过这行代码我们可以看到spring boot把前面创建出来的listeners和命令行参数,传递到prepareEnvironment函数中来准备运行环境。来看一下prepareEnvironment函数的真面目:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {// Create and configure the environmentConfigurableEnvironment environment = getOrCreateEnvironment();configureEnvironment(environment, applicationArguments.getSourceArgs());listeners.environmentPrepared(environment);bindToSpringApplication(environment);if (this.webApplicationType == WebApplicationType.NONE) {environment = new EnvironmentConverter(getClassLoader()).convertToStandardEnvironmentIfNecessary(environment);}ConfigurationPropertySources.attach(environment);return environment;}
在这里我们看到了环境是通过getOrCreateEnvironment创建出来的,再深挖一下getOrCreateEnvironment的源码:
private ConfigurableEnvironment getOrCreateEnvironment() {if (this.environment != null) {return this.environment;}if (this.webApplicationType == WebApplicationType.SERVLET) {return new StandardServletEnvironment();}return new StandardEnvironment();}
通过这段代码我们看到了如果environment 已经存在,则直接返回当前的环境。
小思考:在什么情况下会出现environment 已经存在的情况?提示:我们前面讲过,可以自己初始化SpringApplication,然后调用run函数,在初始化SpringApplication和调用run函数之间,是不是可以发生点什么?
下面的代码判断了webApplicationType是不是SERVLET,如果是,则创建Servlet的环境,否则创建基本环境。我们来挖一挖webApplicationType是在哪里初始化的:
private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."+ "web.reactive.DispatcherHandler";private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."+ "web.servlet.DispatcherServlet";/*** Create a new {@link SpringApplication} instance. The application context will load* beans from the specified primary sources (see {@link SpringApplication class-level}* documentation for details. The instance can be customized before calling* {@link #run(String...)}.* @param resourceLoader the resource loader to use* @param primarySources the primary bean sources* @see #run(Class, String[])* @see #setSources(Set)*/@SuppressWarnings({ "unchecked", "rawtypes" })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 = deduceWebApplicationType();setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}private WebApplicationType deduceWebApplicationType() {if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {return WebApplicationType.REACTIVE;}for (String className : WEB_ENVIRONMENT_CLASSES) {if (!ClassUtils.isPresent(className, null)) {return WebApplicationType.NONE;}}return WebApplicationType.SERVLET;}
通过这段代码,我们发现了原来spring boot是通过检查当前环境中是否存在
org.springframework.web.servlet.DispatcherServlet类来判断当前是否是web环境的。
接着往下看,获得了ConfigurableEnvironment环境以后,通过后面的代码对环境进行“微调”。
通过this.configureIgnoreBeanInfo(environment);如果System中的spring.beaninfo.ignore属性为空,就把当前环境中的属性覆盖上去:
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {if(System.getProperty("spring.beaninfo.ignore") == null) {Boolean ignore = (Boolean)environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);System.setProperty("spring.beaninfo.ignore", ignore.toString());}}
通过Banner printedBanner = this.printBanner(environment);这行代码打印出spring boot的Banner。还记得spring boot启动的时候,在控制台显示的那个图片吗?这里不作深究,继续往下看:
context = this.createApplicationContext();创建了应用上下文:
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."+ "annotation.AnnotationConfigApplicationContext";public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);break;case REACTIVE:contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);}}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, "+ "please specify an ApplicationContextClass",ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);}
通过这里我们看到,spring boot是根据不同的webApplicationType的类型,来创建不同的ApplicationContext的。
总结:通过上面的各种深挖,我们知道了spring boot 2.0中的环境是如何区分普通环境和web环境的,以及如何准备运行时环境和应用上下文。时间不早了,今天就跟大家分享到这里,下一篇文章会继续跟大家分享spring boot 2.0源码的实现。
作者:Lizongshen
出处:http://www.cnblogs.com/lizongshen/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。
spring boot 2.0 源码分析(三)相关推荐
- spring boot 2.0 源码分析(二)
在上一章学习了spring boot 2.0启动的大概流程以后,今天我们来深挖一下SpringApplication实例变量的run函数. 先把这段run函数的代码贴出来: /*** Run the ...
- Spring Boot 2.0系列文章(四):Spring Boot 2.0 源码阅读环境搭建
前提 前几天面试的时候,被问过 Spring Boot 的自动配置源码怎么实现的,没看过源码的我只能投降��了. 这不,赶紧来补补了,所以才有了这篇文章的出现,Spring Boot 2. 0 源码阅 ...
- Spring源码分析(三)
Spring源码分析 第三章 手写Ioc和Aop 文章目录 Spring源码分析 前言 一.模拟业务场景 (一) 功能介绍 (二) 关键功能代码 (三) 问题分析 二.使用ioc和aop重构 (一) ...
- Nouveau源码分析(三):NVIDIA设备初始化之nouveau_drm_probe
Nouveau源码分析(三) 向DRM注册了Nouveau驱动之后,内核中的PCI模块就会扫描所有没有对应驱动的设备,然后和nouveau_drm_pci_table对照. 对于匹配的设备,PCI模块 ...
- Tomcat7.0源码分析——请求原理分析(上)
前言 谈起Tomcat的诞生,最早可以追溯到1995年.近20年来,Tomcat始终是使用最广泛的Web服务器,由于其使用Java语言开发,所以广为Java程序员所熟悉.很多早期的J2EE项目,由程序 ...
- Android6.0源码分析—— Zygote进程分析(补充)
原文地址: http://blog.csdn.net/a34140974/article/details/50915307 此博文为<Android5.0源码分析-- Zygote进程分析> ...
- android6.0源码分析之Camera API2.0下的初始化流程分析
1.Camera2初始化的应用层流程分析 Camera2的初始化流程与Camera1.0有所区别,本文将就Camera2的内置应用来分析Camera2.0的初始化过程.Camera2.0首先启动的是C ...
- RocketMQ4.0源码分析之-路由管理
RocketMQ4.0源码分析之-路由管理 一 前言 路由管理功能是RocketMQ的核心功能之一,涵盖了订阅管理,连接管理,负载均衡管理等一系列功能,代码布在NameServer,Broker,Pr ...
- 【投屏】Scrcpy源码分析三(Client篇-投屏阶段)
Scrcpy源码分析系列 [投屏]Scrcpy源码分析一(编译篇) [投屏]Scrcpy源码分析二(Client篇-连接阶段) [投屏]Scrcpy源码分析三(Client篇-投屏阶段) [投屏]Sc ...
最新文章
- MLIR与Code Generation
- opencv上gpu版surf特征点与orb特征点提取及匹配实例
- 分享文章《控制情绪,享受人生》
- idea启动webservice_Intellij Idea 之 WebService客户端测试
- 使用vbscript脚本调用web服务
- 步骤6 - WebSocket服务器把请求的响应结果推送给webshop
- 团队行为心理学读书笔记(5)执行力背后的行为心理学
- “约见”面试官系列之常见面试题第八篇说说原型与原型链(建议收藏)
- 一个基于typescript、mobx、react16、react-router4、antd的后台模板
- Python基础学习:svn导出差异文件脚本
- trunk vlan 加路由
- windows封装/备份恢复/双系统安装
- 等不到那人,回不到人间——dbGet(四)
- gohost -- go 开发的命令行hosts配置管理工具
- python sklearn: 模型(如 SVM,PCA等)的保存与加载调用
- pxe无盘服务器教程,[教程]Synology+PXE挂载iSCSI网络无盘启动Win7(08.04更新)
- 逆发动机模型_simulink
- OSG给模型贴图显示
- 2007年中考语文模拟试题1
- 神经网络与傅立叶变换有关系吗?
热门文章
- 都客音量调节助手v2.1(win7专用)发布了
- iOS tableView刷新
- 总结ThinkPHP使用技巧经验分享(三)
- 通过demo搞懂encode_utf8和decode_utf8
- Java面向对象编程 第一章 面向对象开发方法概述
- Python+Selenium学习--异常截图
- g++: internal compiler error: Killed (program cc1plus)Please submit a full bug report,内存不足问题解决
- 解决error: Microsoft Visual C++ 14.0 is required 问题
- 解决CentOS6.5下MySQL5.6无法远程连接的问题
- 【干货】如何搭建靠谱的数据仓库.pdf(附下载链接)