项目创建
这里创建项目我们使用IDE的创建方式。当脚手架全部搭建完毕之后,我准备再弄一个项目来按照脚手架的架构创建项目。
这里我使用的IntelliJ IDE。首先我们先点击左上方new一个project,然后选择Spring Initializr,选择JDK版本。这里我本地使用的是JDK1.8。点击下一步
然后我们开始简单的配置项目,这里主要需要更改Group和Artifact。这里既涉及你项目以后的Maven左边,也涉及你的项目名字。
接着选择依赖,注意这里有很多人会选择Lombok。是的,假如我创建一个项目我也会使用lombok编程。但这次我要构建一个通用性强的脚手架工程。而lombok会限制我的版本跨度,所以我不选择lombok。
创建完之后IDE会自动的帮你拉去需要的依赖包。在拉取完之后建议执行maven命令稍微build一下项目,确保没有问题。最后就是运行XXXApplication.java文件来运行服务。当你看如类似如下日志时,这就说明你项目已经创建成功。
SpringBoot项目架构解读
一个基本的SpringBoot项目最基本的需要下面4部分
- Maven项目的pom文件:主要用来引入SpringBoot的依赖
- XXXApplication.java:这是SpringBoot的启动类。最基本可以通过IDE直接run或者打包后执行java -jar。
- 配置文件:SpringBoot的配置文件,里面可以配置各种需要的参数,有点类似以前的xml但得到了很大的简化。具体的配置方式和加载顺序这里不多解释,大家可以去看其他文章
- 测试类:与XXXApplication.java相对应,它是测试类的主入口
SpringBoot启动过程
下面通过解读源码来了解SpringBoot的启动过程。
执行入口
每一个SpringBoot的服务都有一个XXXApplication.java的入口类。而里面提供了一个main方法让我们启动服务。
public static void main(String[] args) {
SpringApplication.run(BasebootApplication.class, args);
}
事实上也是执行了SpringApplication.run方法。并把入口类已经一些所需参数传入到其中。
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
从这里看到,其实SpringBoot的启动其实就是实例化一个SpringApplication;然后调用run方法启动。所以下面我们将继续深入SpringApplication的实例化
SpringApplication实例化
进入这个类的实例化方法,我们可以看到如下代码。我们可以发现,这其实就是准备环境的过程。这里有一个点可以说明一下:
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
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.lazyInitialization = false;
// 资源初始化资源加载器为 null
this.resourceLoader = resourceLoader;
// 断言主要加载资源类不能为 null,否则报错
Assert.notNull(primarySources, "PrimarySources must not be null");
// 初始化主要加载资源类集合并去重
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
// 推断当前 WEB 应用类型
// 值得注意的是,这一段主要判断我们的应用到底是普通servlet还是WebFlux服务(响应式服务),在下面我贴出一段代码
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置应用上线文初始化器
// 有意思的是,这里会正式通过初始化我们项目中经常使用的初始化器。
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 推断主入口应用类
this.mainApplicationClass = this.deduceMainApplicationClass();
}
WebApplicationType 的判断实现。这里可以看到,SpringBoot是通过ClassUitls来判断类型的。
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
String className = var0[var2];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
return SERVLET;
}
}
SpringApplication运行
初始化SpringApplication成功后,我们就开始执行run方法来运行我们的应用了。
public ConfigurableApplicationContext run(String... args) {
// 启动计时监控器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 初始化应用上下文和异常报告集合
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
// 设置系统属性 `java.awt.headless` 的值,默认值为:true
this.configureHeadlessProperty();
// 创建并启动所有 Spring 运行监听器并发布应用启动事件
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
// 初始化默认应用参数类
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 根据运行监听器和初始化的应用参数来准备 Spring 环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
// 根据环境创建Banner 打印类
Banner printedBanner = this.printBanner(environment);
// 创建应用上下文,再熟悉不过的application context
context = this.createApplicationContext();
// 准备异常报告
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 准备应用上下文
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新应用上下文,当执行完这一步之后,SpringBoot应用就基本启动完成
this.refreshContext(context);
// 应用上下文刷新后置处理
this.afterRefresh(context, applicationArguments);
// 停止计时监控类
stopWatch.stop();
// 输出log
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 发布应用上下文启动完成事件
listeners.started(context);
// 执行所有 Runner 运行器
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);
}
}
到这,SpringBoot的启动过程就已经实现了。我们已经可以访问我们自己的应用。
附录
课题目录:https://blog.csdn.net/turkeym4/article/details/106761043
项目地址:https://gitee.com/turkeymz/baseboot