SpringBoot(二)启动原理
SpringApplicationRunListener:接口的作用主要就是在Spring Boot 启动初始化的过程中可以通过SpringApplicationRunListener接口回调来让用户在启动的各个流程中可以加入自己的逻辑

@SpringBootApplication annotation can be used to enable those three features, that is:
@EnableAutoConfiguration: enable Spring Boot’s auto-configuration mechanism(扫描“spring.factories”文件,实现自动注册)@ComponentScan: enable@Componentscan on the package where the application is located (see the best practices)等同于(<compan-scan/>扫描加上注解的类)@Configuration: allow to register extra beans in the context or import additional configuration classes等同于(spring中在spring.xml文件中 写一个<bean/>。bean注册及bean管理)
内置Tomcat
The @SpringBootApplication annotation is equivalent to using @Configuration, @EnableAutoConfiguration, and @ComponentScan with their default attributes, as shown in the following example:
SpringBoot自动配置模块
该配置模块的主要使用到了SpringFactoriesLoader,即Spring工厂加载器,该对象提供了loadFactoryNames方法,入参为factoryClass和classLoader,即需要传入工厂类名称和对应的类加载器,方法会根据指定的classLoader,加载该类加器搜索路径下的指定文件,即spring.factories文件,传入的工厂类为接口,而文件中对应的类则是接口的实现类,或最终作为实现类,所以文件中一般为如下图这种一对多的类名集合,获取到这些实现类的类名后,loadFactoryNames方法返回类名集合,方法调用方得到这些集合后,再通过反射获取这些类的类对象、构造方法,最终生成实例。

SpringBoot自动化配置关键组件关系图
mybatis-spring-boot-starter、spring-boot-starter-web等组件的META-INF文件下均含有spring.factories文件,自动配置模块中,SpringFactoriesLoader收集到文件中的类全名并返回一个类全名的数组,返回的类全名通过反射被实例化,就形成了具体的工厂实例,工厂实例来生成组件具体需要的bean。
之前我们提到了EnableAutoConfiguration注解,其类图如下:

可以发现其最终实现了ImportSelector(选择器)和BeanClassLoaderAware(bean类加载器中间件),重点关注一下AutoConfigurationImportSelector的selectImports方法。


MybatisAutoConfiguration 在上面的代码可以看到自动配置器会根据传入的factoryClass.getName()到项目系统路径下所有的spring.factories文件中找到相应的key,从而加载里面的类。我们就选取这个mybatis-spring-boot-autoconfigure下的spring.factories文件

发现Spring的@Configuration,俨然是一个通过注解标注的springBean,继续向下看,
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class}):当存在SqlSessionFactory.class,SqlSessionFactoryBean.class这两个类时才解析MybatisAutoConfiguration配置类,否则不解析这一个配置类,make sence,我们需要mybatis为我们返回会话对象,就必须有会话工厂相关类。@CondtionalOnBean(DataSource.class):只有处理已经被声明为bean的dataSource。@ConditionalOnMissingBean(MapperFactoryBean.class)这个注解的意思是如果容器中不存在name指定的bean则创建bean注入,否则不执行(该类源码较长,篇幅限制不全粘贴)
以上配置可以保证sqlSessionFactory、sqlSessionTemplate、dataSource等mybatis所需的组件均可被自动配置,@Configuration注解已经提供了Spring的上下文环境,所以以上组件的配置方式与Spring启动时通过mybatis.xml文件进行配置起到一个效果。
通过分析我们可以发现,只要一个基于SpringBoot项目的类路径下存在SqlSessionFactory.class, SqlSessionFactoryBean.class,并且容器中已经注册了dataSourceBean,就可以触发自动化配置,意思说我们只要在maven的项目中加入了mybatis所需要的若干依赖,就可以触发自动配置,但引入mybatis原生依赖的话,每集成一个功能都要去修改其自动化配置类,那就得不到开箱即用的效果了。

这里是截取的mybatis-spring-boot-starter的源码中pom.xml文件中所有依赖:

因为maven依赖的传递性,我们只要依赖starter就可以依赖到所有需要自动配置的类,实现开箱即用的功能。也体现出Springboot简化了Spring框架带来的大量XML配置以及复杂的依赖管理,让开发人员可以更加关注业务逻辑的开发。
SpringBoot 内置tomcat
@SpringBootApplication->,@EnableAutoConfiguration->@ComponentScan,@EnableAutoConfiguration->AutoConfigurationImportSelector:selectImports()
-getAutoConfigurationEntry()->getCandidateConfigurations():
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
autoconfig.jar 下面 spring-boot-autoconfigure-2.1.1.RELEASE.jar->spring.factories
ServletWebServerFactoryAutoConfiguration(生产出一个servlet容器)):
EmbeddedTomcat->TomcatServletWebServerFactory->getWebServer
public WebServer getWebServer(ServletContextInitializer... initializers) {
//等于创建了server 里面在添加Connector,host 等
Tomcat tomcat = new Tomcat();
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
return this.getTomcatWebServer(tomcat);
}
Tomcat 在什么时候被创建的?
SpringApplication.run(SpringbootProviderApplication.class, args)->refreshContext()->onRefresh->
ServletWebServerApplicationContext->createWebServer()->
protected void onRefresh() {
super.onRefresh();
try {
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = this.getServletContext();
if (webServer == null && servletContext == null) {
//在加载的时候getWebServerFactory 已经加载了
ServletWebServerFactory factory = this.getWebServerFactory();
//创建webServer ServletWebServerFactory 有不同的实现类 如 tomcat ,jetty的
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
} else if (servletContext != null) {
try {
this.getSelfInitializer().onStartup(servletContext);
} catch (ServletException var4) {
throw new ApplicationContextException("Cannot initialize servlet context", var4);
}
}
this.initPropertySources();
}

SpringApplication.run()

Spring Context IOC 容器 (spring所有初始化的操作)
Listener
Filter
1.创建SpringApplication应用
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.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//判断web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//初始化
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设计监听器 SpringFactories-> spring.factories文件
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
2.run (以下源码删除了部分代码)
public ConfigurableApplicationContext run(String... args) {
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = this.printBanner(environment);
// spring上下文
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 在该方法中加载tomcat :AbstractApplicationContext->refresh——>onRefresh(创建web容器)
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
创建web容器即Tomcat
protected void onRefresh() {
super.onRefresh();
try {
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
spring-boot-starter?
SpringBoot(二)启动原理的更多相关文章
- (转)springboot应用启动原理(二) 扩展URLClassLoader实现嵌套jar加载
转:https://segmentfault.com/a/1190000013532009 在上篇文章<springboot应用启动原理(一) 将启动脚本嵌入jar>中介绍了springb ...
- springboot之启动原理解析
前言 SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开SpringBoot的神秘面 ...
- springboot之启动原理解析及源码阅读
前言 SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开SpringBoot的神秘面 ...
- SpringBoot FatJar启动原理
目录 SpringBoot FatJar启动原理 背景 储备知识 URLStreamHandler Archive 打包 SpringBoot启动 扩展 SpringBoot FatJar启动原理 背 ...
- 【SpringBoot】 理解SpringBoot的启动原理
前言 前文已经介绍了Spring Bean的生命周期,那么使用过程中发现SpringBoot 的启动非常快捷,本文将介绍SpringBoot的内部启动原理. 启动过程 如上图所示,我们先分析下Spri ...
- (转)springboot应用启动原理(一) 将启动脚本嵌入jar
转:https://segmentfault.com/a/1190000013489340 Spring Boot Takes an opinionated view of building prod ...
- 【转】Spring Boot干货系列:(三)启动原理解析
前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开Sprin ...
- Spring Boot干货系列:(三)启动原理解析
Spring Boot干货系列:(三)启动原理解析 2017-03-13 嘟嘟MD 嘟爷java超神学堂 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说 ...
- Spring Boot启动原理解析
Spring Boot启动原理解析http://www.cnblogs.com/moonandstar08/p/6550758.html 前言 前面几章我们见识了SpringBoot为我们做的自动配置 ...
- 5、Spring Boot 2.x 启动原理解析
1.5 Spring Boot 启动原理解析 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏 ...
随机推荐
- php curl 传递数据
<?php header("Content-type: text/html; charset=utf-8"); /** * curl 传递数据 */ class curl { ...
- 如何用Xshell导出文件到桌面本地
在软件开发中,会经常用到登录到Linux服务器,查看相关日志,同时也会远程取出文件到本地环境, 在没有xftp客户端的情况下,如何直接使用xshell软件直接下载文件到本地呢 下载文件: 使用sz命令 ...
- dell如何安装Win10/Ubuntu双系统
原文:https://www.cnblogs.com/askDing/p/10477345.html 测试环境: DELL PRECISION 7510: CPU:Intel Core i5-6300 ...
- Jenkins 获取 Git 的提交记录(Change Log)
工作中用 Jenkins 做 iOS 和 Android 的持续集成,之前实现的是当 git 有新代码提交的时候,就会自动编译并上传安装包到蒲公英,然后自动发送QQ群通知或者讨论组通知给相关小伙伴,方 ...
- 20175313 张黎仙《获奖感想与Java阶段性学习总结》
一.获奖感想 很荣幸能够成为为数不多的小黄衫获得者之一,这是对我近一学期以来学习成果的肯定,也激励着我更加努力学习. 首先我要感谢的人就是娄嘉鹏老师.我曾在师生关系中提到,我认为的好老师的特点之一是: ...
- XGBoost、LightGBM、Catboost总结
sklearn集成方法 bagging 常见变体(按照样本采样方式的不同划分) Pasting:直接从样本集里随机抽取的到训练样本子集 Bagging:自助采样(有放回的抽样)得到训练子集 Rando ...
- 【Java】 HashMap
Java HashMap 标签(空格分隔): Java source-code hash-map 总结 HashTable的基本数据结构 Entry的hash与table的长度计算indexFor才能 ...
- Flutter移动电商实战 --(46)详细页_自定义TabBar Widget
主要实现详情和评论的tab provide定义变量 自己做一个tab然后用provide去控制 定义两个变量来判断是左侧选中了还是右侧选中了.并定义一个方法来接受参数,修改是左侧还是右侧选中的状态值 ...
- Matlab 图像平移、旋转、缩放、镜像
今天学习了用Matlab实现对图像的基本操作.在Matlab中,图像是按照二维矩阵的形式表示的.所以对图像的操作就是对矩阵的操作. 对图像进行缩放.平移.旋转,都可以转化为矩阵的运算. 关于变换矩阵的 ...
- React 高阶组件浅析
高阶组件的这种写法的诞生来自于社区的实践,目的是解决一些交叉问题(Cross-Cutting Concerns).而最早时候 React 官方给出的解决方案是使用 mixin .而 React 也在官 ...