springboot底层机制实现
⬅️ 上一篇: springboot系列四: sprintboot容器功能
🎉 欢迎来到 springboot系列五: springboot底层机制实现 上 🎉
在本篇文章中,我们将深入探讨 Spring Boot 的底层机制。通过理解这些底层机制,您可以更好地掌握 Spring Boot 的工作原理,并在项目中应用这些知识。
🔧 本篇需要用到的项目: zzw-springboot项目
搭建SpringBoot底层机制开发环境
1.创建 Maven 项目 zzw-springboot, 参考springboot快速入门
2.在pom.xml引入SpringBoot父工程和web项目场景启动器
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zzw</groupId>
<artifactId>quickStart</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!--导入springboot父工程-规定写法[在mybatis中讲过]-->
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.5.3</version>
</parent>
<!--导入web项目场景启动器: 会自动导入和web开发相关的所有依赖[库/jar]
后面还会说明spring-boot-starter-web 到底引入哪些相关依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
3.创建com.zzw.springboot.MainApp.java
SpringBoot引用主程序
/**
* @SpringBootApplication: 表示这是一个springboot引用/项目
*/
@SpringBootApplication
public class MainApp {
public static void main(String[] args) {
//启动springboot应用程序/项目
//提示问题:当我们执行run方法时,怎么就启动了我们的内置的Tomcat?
//在分析run方法底层机制的基础上, 我们自己尝试实现
ApplicationContext ioc =
SpringApplication.run(MainApp.class, args);
}
}
4.启动项目 ok, 大家注意Tomcat也启动了.[这里请同学们先思考, 是如何实现的?]
@Configuration+@Bean会发生什么,并分析机制
1.创建D:\idea_project\zzw_springboot\zzw-springboot\src\main\java\com\zzw\springboot\bean\Dog.java
public class Dog {}
2.创建com.zzw.springboot.config
/**
* @Configuration:指明当前类是一个配置类;充当Spring容器/Spring配置文件
* 如果该配置类, 在springboot扫描的包/子包下, 会被注入到Spring容器中
* 在该类中, 可以通过@Bean 来注入其它的组件
*/
@Configuration
public class Config {
/**
* 1.通过@Bean的方式, 将new出来的Bean对象, 放入到Spring容器
* 2.该bean在Spring容器的name/id 默认就是 方法名
* 3.通过方法名, 可以得到注入到容器中的dog对象
*/
@Bean
public Dog dog() {
return new Dog();
}
}
3.修改com.zzw.springboot.MainApp.java
, 看看容器中是否已经注入了 dog 实例
debug
ioc容器中已经注入了 dog 实例
@Configuration注解标注的配置类也会被实例化并被注入到Spring容器.
4.底层机制分析: 仍然是 我们实现Spring容器那一套机制 IO/文件扫描+注解+反射+集合+映射. 提示: 回去复习一下 实现Spring底层机制.
(1)快捷键查看ioc->singletonObjects->table列表 有多少元素? table右键->Customize Data Views->Enable alternative view for… 但是勾选上会少很多东西
这里是按照索引排序,无法按照字母排序。
(2)debug时元素按照字母排列
提出问题: SpringBoot 是怎么启动Tomcat, 并可以支持访问@Controller
1.创建com.zzw.springboot.controller.HiController
RestController解释, 手动实现Tomcat底层机制
/**
* 1.因为 HiController 被 @RestController 标注, 所以就是一个控制器
* 2.HiController 在扫描包/子包下,就会被注入到Spring容器
*/
@RestController
public class HiController {
@RequestMapping("/hi")
public String hi() {
return "hi zzw HiController";
}
}
2.完成测试, 浏览器输入 http://localhost:8080/hi
3.提出问题: SpringBoot 是怎么内嵌 Tomcat, 并启动Tomcat的? => 追踪源码
源码分析: SpringApplication.run()
1.Debug SpringApplication.run(MainApp.class, args);
看看SpringBoot 是如何启动Tomcat的.
2.我们的Debug目标: 紧抓一条线, 就是看到 tomcat 被启动的代码, 比如 tomcat.start().
3.源码如下
@SpringBootApplication
public class MainApp {
public static void main(String[] args) {
//启动springboot应用程序/项目
//提示问题:当我们执行run方法时,怎么就启动了我们的内置的Tomcat?
//在分析run方法底层机制的基础上, 我们自己尝试实现
ApplicationContext ioc =
SpringApplication.run(MainApp.class, args);
/**
* 这里我们开始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
* public ConfigurableApplicationContext run(String... args) {
* StopWatch stopWatch = new StopWatch();
* stopWatch.start();
* DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
* ConfigurableApplicationContext context = null;
* this.configureHeadlessProperty();
* SpringApplicationRunListeners listeners = this.getRunListeners(args);
* listeners.starting(bootstrapContext, this.mainApplicationClass);
*
* try {
* ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
* ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
* this.configureIgnoreBeanInfo(environment);
* Banner printedBanner = this.printBanner(environment);
* context = this.createApplicationContext();//严重分析: 创建容器
* context.setApplicationStartup(this.applicationStartup);
* this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
* this.refreshContext(context);//严重分析: 刷新应用程序上下文, 比如 初始化默认设置/注入相关bean/启动tomcat
* this.afterRefresh(context, applicationArguments);
* stopWatch.stop();
* 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, listeners);
* throw new IllegalStateException(var10);
* }
*
* try {
* listeners.running(context);
* return context;
* } catch (Throwable var9) {
* this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
* throw new IllegalStateException(var9);
* }
* }
* 4.SpringApplication.java: 容器类型很多, 会根据你的this.webApplicationType创建对应的容器
* this.webApplicationType 默认是 Servlet, 也就是web容器, 可以处理servlet
* protected ConfigurableApplicationContext createApplicationContext() {
* return this.applicationContextFactory.create(this.webApplicationType);
* }
* 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 var2) {
* throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var2);
* }
* };
* 6.SpringApplication.java
* private void refreshContext(ConfigurableApplicationContext context) {
* if (this.registerShutdownHook) {
* shutdownHook.registerApplicationContext(context);
* }
*
* this.refresh(context);//严重分析, 真正执行相关任务
* }
* 7.SpringApplication.java
* protected void refresh(ConfigurableApplicationContext applicationContext) {
* applicationContext.refresh();
* }
* 8.ServletWebServerApplicationContext.java
* public final void refresh() throws BeansException, IllegalStateException {
* try {
* super.refresh();//分析这个方法
* } catch (RuntimeException var3) {
* WebServer webServer = this.webServer;
* if (webServer != null) {
* webServer.stop();
* }
*
* throw var3;
* }
* }
* 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.ServletWebServerApplicationContext.java
* protected void onRefresh() {
* super.onRefresh();
*
* try {
* this.createWebServer();//看到胜利的曙光. 创建webserver 可以理解成会创建指定web服务-Tomcat
* } catch (Throwable var2) {
* throw new ApplicationContextException("Unable to start web server", var2);
* }
* }
* 11.ServletWebServerApplicationContext.java
* private void createWebServer() {
* WebServer webServer = this.webServer;
* ServletContext servletContext = this.getServletContext();
* if (webServer == null && servletContext == null) {
* StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
* ServletWebServerFactory factory = this.getWebServerFactory();
* createWebServer.tag("factory", factory.getClass().toString());
* this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});//严重分析, 使用TomcatServletWebServerFactory 创建一个TomcatWebServer
* createWebServer.end();
* this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
* this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
* } else if (servletContext != null) {
* try {
* this.getSelfInitializer().onStartup(servletContext);
* } catch (ServletException var5) {
* throw new ApplicationContextException("Cannot initialize servlet context", var5);
* }
* }
*
* this.initPropertySources();
* }
* 12.TomcatServletWebServerFactory.java: 会创建Tomcat, 并启动
* public WebServer getWebServer(ServletContextInitializer... initializers) {
* if (this.disableMBeanRegistry) {
* Registry.disableRegistry();
* }
*
* Tomcat tomcat = new Tomcat();//创建了Tomcat对象
* File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
* //这里做了一系列配置
* tomcat.setBaseDir(baseDir.getAbsolutePath());
* Connector connector = new Connector(this.protocol);
* connector.setThrowOnFailure(true);
* 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);//严重分析该方法.
* }
* 13.TomcatServletWebServerFactory.java
* protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
* return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());
* }
* 14.TomcatServletWebServerFactory.java
* public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
* this.monitor = new Object();
* this.serviceConnectors = new HashMap();
* Assert.notNull(tomcat, "Tomcat Server must not be null");
* this.tomcat = tomcat;
* this.autoStart = autoStart;
* this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null;
* this.initialize();//分析这个方法
* }
* 15.TomcatServletWebServerFactory.java
* private void initialize() throws WebServerException {
* logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
* Object var1 = this.monitor;
* synchronized(this.monitor) {
* try {
* this.addInstanceIdToEngineName();
* Context context = this.findContext();
* context.addLifecycleListener((event) -> {
* if (context.equals(event.getSource()) && "start".equals(event.getType())) {
* this.removeServiceConnectors();
* }
*
* });
* this.tomcat.start();//启动Tomcat
* this.rethrowDeferredStartupExceptions();
*
* try {
* ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
* } catch (NamingException var5) {
* ;
* }
*
* this.startDaemonAwaitThread();
* } catch (Exception var6) {
* this.stopSilently();
* this.destroySilently();
* throw new WebServerException("Unable to start embedded Tomcat", var6);
* }
*
* }
* }
*/
System.out.println("ok.");
}
}
SpringBoot的debug流程
F7, Step Into
F7, Step Into
F7, Step Into
F7, Step Into
webApplicationType类型可以在配置文件中修改
F7, Step Into
Shift+F8, Step Out
F7, Step Into
F7, Step Into
F7, Step Into
看看父类的类型
F7, Step Into
F7, Step Into
F7, Step Into
F7, Step Into
F7, Step Into
放行 Resume Problem
F7, Step Into
放行 Resume Problem
对容器进行估值
🔜 下一篇预告: springboot系列六: springboot底层机制实现 下
📚 目录导航 📚
- springboot系列一: springboot初步入门
- springboot系列二: sprintboot依赖管理
- springboot系列三: sprintboot自动配置
- springboot系列四: sprintboot容器功能
- springboot系列五: springboot底层机制实现 上
- springboot系列六: springboot底层机制实现 下
- springboot系列七: Lombok注解,Spring Initializr,yaml语法
…
💬 读者互动 💬
在探索 Spring Boot 底层机制的过程中,您有哪些收获或遇到过哪些挑战?欢迎在评论区留言,让我们一起讨论吧!😊