您现在的位置是:首页 >技术教程 >分析SpringBoot 底层机制【Tomcat 启动分析+Spring 容器初始化+Tomcat 如何关联Spring 容器之源码分析网站首页技术教程
分析SpringBoot 底层机制【Tomcat 启动分析+Spring 容器初始化+Tomcat 如何关联Spring 容器之源码分析
目录
分析SpringBoot 底层机制【Tomcat 启动分析+Spring 容器初始化+Tomcat 如何关联Spring 容器之源码分析
启动项目ok, 大家注意Tomcat 也启动了[这里思考, 是如何实现的?]
@Configuration + @Bean 会发生什么,并分析机制
MainApp.java, 看看容器中是否已经注入了dog 实例
提出问题:SpringBoot 是怎么启动Tomcat ,并可以支持访问@Controller
问题: SpringBoot 是怎么内嵌Tomcat, 并启动Tomcat 的? =>追踪源码
1. 这里我们开始Debug SpringApplication.run()
2.SpringApplication.java : 创建返回 ConfigurableApplicationContext对象
5. ApplicationContextFactory.java
6.然后返回到第3步骤然后我们在直接进去 refreshContext(context);
8.applicationContext.refresh();直接进入
9.然后直接进入super.refresh();到AbstractApplicationContext.java
12.直接进入 this.webServer = factory.getWebServer(getSelfInitializer());
13.直接进入 getTomcatWebServer(tomcat);
14.直接进入TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
分析SpringBoot 底层机制【Tomcat 启动分析+Spring 容器初始化+Tomcat 如何关联Spring 容器之源码分析
搭建SpringBoot 底层机制开发环境
创建Maven 项目wyx-springboot
步骤就不再展示了 在我的博客有
修改pom.xml , 导入相关依赖
<!-- 导入springboot 父工程,规定的写法-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
</parent>
<!-- 导入web 项目场景启动器,会自动导入和web 开发相关依赖,非常方便-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
创建MainApp.java
@SpringBootApplication
public class MainApp {
public static void main(String[] args) {
//启动SpringBoot 应用程序
ConfigurableApplicationContext run =
SpringApplication.run(MainApp.class, args);
}
}
启动项目ok, 大家注意Tomcat 也启动了[这里思考, 是如何实现的?]
@Configuration + @Bean 会发生什么,并分析机制
创建dog对象
public class Dog {
}
创建Config.java类
@Configuration
public class Config {
/**
* 1. 通过@Bean 的方式, 将new 出来的Bean 对象, 放入到Spring 容器
* 2. 该bean 在Spring 容器的name 就是方法名
* 3. 通过方法名, 可以得到new Dog()
* @return
*/
@Bean
public Dog dog() {
return new Dog();
}
}
MainApp.java, 看看容器中是否已经注入了dog 实例
底层机制分析:
仍然是我们实现Spring 容器那一套机制IO/文件扫描+注解+反射+集合+映射, 提示: 回去看博客-"实现Spring 底层机制程序" ,
提出问题:SpringBoot 是怎么启动Tomcat ,并可以支持访问@Controller
创建HiController.java
@RestController
public class HiController {
@RequestMapping("/hi")
public String hi() {
System.out.println("hi i am HiController");
return "hi i am HiController";
}
}
问题: SpringBoot 是怎么内嵌Tomcat, 并启动Tomcat 的? =>追踪源码
源码分析: SpringApplication.run()
1、Debug SpringApplication.run(MainApp.class, args) 看看SpringBoot 是如何启动Tomcat 的.
2、我们的Debug 目标: 紧抓一条线, 就是看到tomcat 被启动的代码. 比如tomcat.start()
1. 这里我们开始Debug SpringApplication.run()
1. SpringApplication.java
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
这里我们就直接进入
2.SpringApplication.java : 创建返回 ConfigurableApplicationContext对象
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
这里我们还是就直接进入
3. 还在SpringApplication.java
1.先进去创建容器
2.然后在进去刷新上下文
3. SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext(); //严重分析: 创建容器
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context); //严重分析: 刷新应用程序上下文,比如 初始化默认设置/注入相关Bean/启动tomcat
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, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
4. SpringApplication.java : 容器类型很多,会根据你的this.webApplicationType创建对应的容器默认 this.webApplicationType 是 SERVLET 也就是web容器/可以处理servlet
4. SpringApplication.java : 容器类型很多,会根据你的this.webApplicationType创建对应的容器
默认 this.webApplicationType 是 SERVLET 也就是web容器/可以处理servlet
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
这里我们还是就直接进入
5. ApplicationContextFactory.java
5. ApplicationContextFactory.java
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
switch (webApplicationType) {
case SERVLET://默认是进入到这个分支 ,返回AnnotationConfigServletWebServerApplicationContext容器
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
} }
catch (Exception ex) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, "
+ "you may need a custom ApplicationContextFactory", ex);
}
};
6.然后返回到第3步骤然后我们在直接进去 refreshContext(context);
6. SpringApplication.java
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
shutdownHook.registerApplicationContext(context);
}
refresh(context); //严重分析,真正执行相关任务
}
7. 直接进入refresh(context);
7. SpringApplication.java
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
8.applicationContext.refresh();直接进入
8. ServletWebServerApplicationContext.java
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();//分析这个方法
}
catch (RuntimeException ex) {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.stop();
}
throw ex;
}
}
9.然后直接进入super.refresh();到AbstractApplicationContext.java
9. AbstractApplicationContext.java
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
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.
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();
}
}
}
10.直接进入onRefresh();
10. ServletWebServerApplicationContext.java
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();//看到胜利的曙光,创建webserver 可以理解成会创建指定web服务-Tomcat
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
11.直接进入 createWebServer();
11. ServletWebServerApplicationContext.java
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
ServletWebServerFactory factory = getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
this.webServer = factory.getWebServer(getSelfInitializer());//严重分析,使用TomcatServletWebServerFactory 创建一个TomcatWebServer
createWebServer.end();
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources(); }
12.直接进入 this.webServer = factory.getWebServer(getSelfInitializer());
12. TomcatServletWebServerFactory.java 会创建Tomcat 并启动Tomcat
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();//创建了Tomcat对象
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
//做了一系列的设置
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat); //严重分析该方法.
}
13.直接进入 getTomcatWebServer(tomcat);
13. TomcatServletWebServerFactory.java , 这里做了校验创建 TomcatWebServer
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}
14.直接进入TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
14. TomcatServletWebServerFactory.java
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
initialize();//分析这个方法.
}
15. 直接进入initialize();
15.TomcatServletWebServerFactory.java
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
addInstanceIdToEngineName();
Context context = findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
// Remove service connectors so that protocol binding doesn't
// happen when the service is started.
removeServiceConnectors();
} });
// Start the server to trigger initialization listeners
this.tomcat.start(); //启动Tomcat
// We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
}
catch (NamingException ex) {
// Naming is not enabled. Continue
}
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();
}
catch (Exception ex) {
stopSilently();
destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
到处我们就全部debug到了下面我就就自己实现