下面这段代码是Upupor网站的后端服务启动代码,我之前一直很好奇Spring是如何启动并对外提供服务的,Spring做了哪些工作?现在就从启动代码开始,一步一步开始跟着源码看看发生了些什么
@MapperScan("com.upupor.dao.mapper")
@SpringBootApplication(scanBasePackages = "com.upupor")
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 21600)
@EnableAsync
@Slf4j
public class UpuporAppApplication {
public static void main(String[] args) {
// 先进入 run 方法
SpringApplication.run(UpuporAppApplication.class, args);
}
}
run(Class<?>[] primarySources, String[] args)方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 这里是新建了一个SpringApplication的应用程序,并把启动类作为参数,加上外界的args参数进来,进行启动前的
// 物料准备工作
return (new SpringApplication(primarySources)).run(args);
}
下面看下SpringApplication
的有参构造
// 有参构造
public SpringApplication(Class<?>... primarySources) {
// 调用下面的 构造函数
this((ResourceLoader)null, primarySources);
}
// SpringApplication 启动的有参构造
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 资源
this.sources = new LinkedHashSet();
// 广告栏Banner样式,默认是控制台输出
this.bannerMode = Mode.CONSOLE;
// 日志
this.logStartupInfo = true;
// 添加命令行属性
this.addCommandLineProperties = true;
/ 添加转换服务
this.addConversionService = true;
// 是否进行Headless的配置,可以理解为获取一些系统的基本参数
this.headless = true;
// 注册关机钩子
this.registerShutdownHook = true;
// 额外资料
this.additionalProfiles = new HashSet();
// 是否是自定义环境
this.isCustomEnvironment = false;
// 懒加载
this.lazyInitialization = 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));
// [重点] 监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// [重点] 主应用程序类
this.mainApplicationClass = this.deduceMainApplicationClass();
}
看完了SpringApplication类的构造,Spring的预备工作也准好了,接下来就看看run方法吧
/**
* 静态帮助类可用于运行Spring程序从指定的资源文件并使用默认的设置和用户提供的参数
*/
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
/**
* 运行Spring应用程序,创建和刷新一个新的应用程序上下文
*/
public ConfigurableApplicationContext run(String... args) {
// 启动计时器
StopWatch stopWatch = new StopWatch();
// 开始计时
stopWatch.start();
// 可配置的应用程序上下文
ConfigurableApplicationContext context = null;
// 初始化SpringBoot异常报告器
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置Headless的配置,可以理解为获取一些系统的基本参数
configureHeadlessProperty();
// 获取Spring应用程序启动的监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 启动监听器
listeners.starting();
try {
// 应用程序参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 根据监听器及应用程序参数进行环境的准备工作
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 配置忽略Bean信息
configureIgnoreBeanInfo(environment);
// 打印Banner
Banner printedBanner = printBanner(environment);
// [重点] 创建应用程序上下文,这里是根据 webApplicationType 来创建对应的应用程序
context = createApplicationContext();
// 异常报告器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// [重点]准备上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// [重点]刷新上下文
refreshContext(context);
// [重点]刷新上下文后续操作
afterRefresh(context, applicationArguments);
// 启动结束,结束计时
stopWatch.stop();
if (this.logStartupInfo) {
// 如果开启了启动日志,则打印日志
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 启动监听器
listeners.started(context);
// [重点]此时开始运行程序(call回调)
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 如果处理有异常,将异常上报给 exceptionReporters
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 监听器运行,监听整个上下文
listeners.running(context);
}
catch (Throwable ex) {
// 如果处理有异常,将异常上报给 exceptionReporters
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
prepareContext(context, environment, listeners, applicationArguments, printedBanner) 源码
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 设置环境
context.setEnvironment(environment);
// 后处理应用程序
postProcessApplicationContext(context);
// 应用初始化
applyInitializers(context);
//
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
postProcessApplicationContext方法
/**
* Apply any relevant post processing the {@link ApplicationContext}. Subclasses can
* apply additional processing as required.
* @param context the application context
*/
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
// 根据Bean的名称生成
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
// 资源加载器
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
// 添加转换服务
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
refreshContext,刷新上下文
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
afterRefresh,无任何实现
/**
* Called after the context has been refreshed.
* @param context the application context
* @param args the application arguments
*/
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
callRunners,方法
private void callRunners(ApplicationContext context, ApplicationArguments args) {
// 初始化运行者
List<Object> runners = new ArrayList<>();
// 从前面准备好的context上下文中,根据Type获取Beans
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 排序
AnnotationAwareOrderComparator.sort(runners);
// 运行
for (Object runner : new LinkedHashSet<>(runners)) {
// 应用程序
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
// 命令行
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}