1、创建Maven 项目nlc-springboot
2、修改nlc-springboot\pom.xml , 导入相关依赖
<groupId>com.nlcgroupId>
<artifactId>nlc-springbootartifactId>
<version>1.0-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.5.3version>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
3 、创建nlc-springboot\src\main\java\com\nlc\springboot\MainApp.java
@SpringBootApplication
public class MainApp {
public static void main(String[] args) {
//启动SpringBoot 应用程序
ConfigurableApplicationContext run = SpringApplication.run(MainApp.class, args);
}
}
4、启动项目ok, 大家注意Tomcat 也启动了
1 、创建nlc-springboot\src\main\java\com\nlc\springboot\bean\Dog.java
public class Dog {
}
2 、创建nlc-springboot\src\main\java\com\nlc\springboot\config\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();
}
}
3 、Debug:nlc-springboot\src\main\java\com\nlc\springboot\MainApp.java, 看看容器中是否已经注入了dog 实例
4、底层机制分析: 仍然是我们实现Spring 容器那一套机制IO/文件扫描+注解+反射+集合+映射
1 、创建nlc-springboot\src\main\java\com\nlc\springboot\controller\HiController.java
@RestController
public class HiController {
@RequestMapping("/hi")
public String hi() {
System.out.println("hi i am HiController");
return "hi i am HiController";
}
}
2、完成测试, 浏览器输入http://localhost:8080/hi
3、问题: SpringBoot 是怎么内嵌Tomcat, 并启动Tomcat 的? =>追踪源码
1、Debug SpringApplication.run(MainApp.class, args) 看看SpringBoot 是如何启动Tomcat 的.
2、我们的Debug 目标: 紧抓一条线, 就是看到tomcat 被启动的代码. 比如tomcat.start()
public class MainApp {
public static void main(String[] args) {
//启动springboot应用程序/项目
//当我们执行run方法时,怎么就启动我们的内置的tomcat?
//在分析run方法的底层机制的基础上,我们自己尝试实现
ConfigurableApplicationContext 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) {//如果想要更改可以通过配置文件更改想要创建的容器种类,但是目前本身就是web开发,没必要去改
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();//如果出现异常就关闭服务,所以 super.refresh()里面肯定存在tomcat启动代码
}
throw var3;
}
}
*
* 9.AbstractApplicationContext.java
* public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();//有点像模板模式,先回到父类做一些共同的工作,因为下面有很多容器都会做共同的工作,
到真正要刷新的时候又通过动态绑定机制回到子类,再开始进行具体的刷新任务
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var10) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
}
this.destroyBeans();
this.cancelRefresh(var10);
throw var10;
} finally {
this.resetCommonCaches();
contextRefresh.end();
}
}
}
*
*10.ServletWebServerApplicationContext.Java
* protected void onRefresh() {
super.onRefresh();
try {
this.createWebServer();//创建webservlet 可以理解成创建指定 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创建一个TomcatWebServlet
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
* 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//做了一个校验创建TomcatWebServer
* 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));
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);
}
}
}
*执行到this.tomcat.start();我们可以返回如下图位置查看context容器值
*/
System.out.println("hello ioc");
当我们刷新整个上下文时像config的配置类进去了,包括我们的Bean也进去了