本文主要是介绍[Spring源码] 浅析 SpringApplication`的构造方法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- `SpringApplication`的构造方法
- 获取 Bean Definition 源
- 推断应用类型
- 添加 ApplicationContext 初始化器
- 添加事件监听器
- 主类推断
SpringApplication
的构造方法
Springboot
的主启动类为:
@SpringBootApplication
public class BootApplication {public static void main(String[] args) {SpringApplication.run(BootApplication.class, args);}}
其中 SpringApplication#run()
方法 调用的是如下静态方法:
/*** Static helper that can be used to run a {@link SpringApplication} from the* specified source using default settings.* @param primarySource the primary source to load* @param args the application arguments (usually passed from a Java main method)* @return the running {@link ApplicationContext}*/public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);}/*** Static helper that can be used to run a {@link SpringApplication} from the* specified sources using default settings and user supplied arguments.* @param primarySources the primary sources to load* @param args the application arguments (usually passed from a Java main method)* @return the running {@link ApplicationContext}*/public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);}
最终使用 new 关键字构造了 SpringApplication
对象,然后调用了非静态 run()
方法。
/*** Run the Spring application, creating and refreshing a new* {@link ApplicationContext}.* @param args the application arguments (usually passed from a Java main method)* @return a running {@link ApplicationContext}*/public ConfigurableApplicationContext run(String... args) {Startup startup = Startup.create();if (this.registerShutdownHook) {SpringApplication.shutdownHook.enableShutdownHookAddition();}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);Banner printedBanner = printBanner(environment);context = createApplicationContext();context.setApplicationStartup(this.applicationStartup);prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);refreshContext(context);afterRefresh(context, applicationArguments);startup.started();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startup);}listeners.started(context, startup.timeTakenToStarted());callRunners(context, applicationArguments);}catch (Throwable ex) {...}try {if (context.isRunning()) {listeners.ready(context, startup.ready());}}catch (Throwable ex) {...}return context;}
构造 SpringApplication 对象时做了如下几件事:
- 获取 Bean Definition 源
- 推断应用类型
- 添加 ApplicationContext 初始化器
- 添加事件监听器
- 主类推断
获取 Bean Definition 源
package com.example.boot;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;import java.util.Arrays;
import java.util.Collections;@SpringBootApplication
public class BootApplication {public static void main(String[] args) {SpringApplication spring = new SpringApplication(BootApplication.class);// 设置 xml 的beanspring.setSources(Collections.singleton("classpath:bean.xml"));// 创建并初始化 Spring 容器ConfigurableApplicationContext context = spring.run(args);Arrays.stream(context.getBeanDefinitionNames()).forEach(i -> {System.out.println("name: " + i +" 来源: " + context.getBeanFactory().getBeanDefinition(i).getResourceDescription());});context.close();}static class Bean1 {}static class Bean2 {}@Beanpublic Bean2 bean2() {return new Bean2();}}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:util="http://www.springframework.org/schema/util"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-4.0.xsd"
><bean id="bean1" class="com.example.boot.BootApplication.Bean1" />
</beans>
输出
...
name: bootApplication 来源: null
name: bean1 来源: class path resource [bean.xml]
name: bean2 来源: com.example.boot.BootApplication...
其中来源为 null
的是 Spring
内置的。
推断应用类型
应用推断主要在 SpringbootApplication
的构造方法中,this.webApplicationType = WebApplicationType.deduceFromClasspath();
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)) {// 非 web return NONE;}}// servletreturn SERVLET;}}
当然,我们可以直接使用反射调用这个静态方法,判断当前应用环境
Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");deduceFromClasspath.setAccessible(true);System.out.println("\t应用类型为: " + deduceFromClasspath.invoke(null));
输出
应用类型为: SERVLET
添加 ApplicationContext 初始化器
调用 SpringApplication
对象的 run()
方法时会创建 ApplicationContext
,最后调用 ApplicationContext 的 refresh()
方法完成初始化。
在创建与初始化完成之间的一些拓展功能就由
ApplicationContext
初始化器完成。
在 SpringApplication
的构造方法中,添加的初始化器信息从配置文件中读取:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {...this.webApplicationType = WebApplicationType.deduceFromClasspath(); 初始化器this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = this.deduceMainApplicationClass();}
当然可以调用 SpringApplication 对象的 addInitializers()
方法添加自定义初始化器:
注意添加初始化器需要在调用
run
方法之前,因为run
方法会refresh
// 初始化器// 这里用到了函数方法,可以简化代码spring.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {System.out.println(">>>>>>>>>>");if( applicationContext instanceof GenericApplicationContext genericApplicationContext) {System.out.println(">>>>> 注册 bean3");genericApplicationContext.registerBean("bean3", Bean3.class);}}});// 创建并初始化 Spring 容器ConfigurableApplicationContext context = spring.run(args);
输出
name: bean3 来源: null
name: bean1 来源: class path resource [bean.xml]
name: bean2 来源: com.example.boot.BootApplication
name: beanNameViewResolver 来源: class path resource [org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]
name: beanNameHandlerMapping 来源: class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]应用类型为: SERVLET
添加事件监听器
与添加 ApplicationContext 初始化器一样,在 SpringApplication 的构造方法中,添加的事件监听器信息从配置文件中读取:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {...this.webApplicationType = WebApplicationType.deduceFromClasspath(); 初始化器this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));// 事件监听器this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = this.deduceMainApplicationClass();}
可以调用 SpringApplication
对象的 addListeners()
方法添加自定义事件监听器:
spring.addListeners(event -> System.out.println("\t事件为: " + event));// 创建并初始化 Spring 容器ConfigurableApplicationContext context = spring.run(args);
输出
2023-12-12 23:08:04.067 INFO 82643 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''事件为: org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent[source=org.springframework.boot.web.embedded.tomcat.TomcatWebServer@54db056b]事件为: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@389c4eb1, started on Tue Dec 12 23:08:03 CST 2023]
2023-12-12 23:08:04.073 INFO 82643 --- [ main] com.example.boot.BootApplication : Started BootApplication in 0.695 seconds (JVM running for 0.9)事件为: org.springframework.boot.context.event.ApplicationStartedEvent[source=org.springframework.boot.SpringApplication@5119fb47]事件为: org.springframework.boot.availability.AvailabilityChangeEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@389c4eb1, started on Tue Dec 12 23:08:03 CST 2023]事件为: org.springframework.boot.context.event.ApplicationReadyEvent[source=org.springframework.boot.SpringApplication@5119fb47]事件为: org.springframework.boot.availability.AvailabilityChangeEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@389c4eb1, started on Tue Dec 12 23:08:03 CST 2023]事件为: org.springframework.boot.availability.AvailabilityChangeEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@389c4eb1, started on Tue Dec 12 23:08:03 CST 2023]事件为: org.springframework.context.event.ContextClosedEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@389c4eb1, started on Tue Dec 12 23:08:03 CST 2023]
主类推断
依然是在 SpringApplication
的构造方法中,有
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {...this.webApplicationType = WebApplicationType.deduceFromClasspath(); 初始化器this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));// 事件监听器this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));// 主类推断this.mainApplicationClass = this.deduceMainApplicationClass();}
private Class<?> deduceMainApplicationClass() {try {StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();StackTraceElement[] var2 = stackTrace;int var3 = stackTrace.length;for(int var4 = 0; var4 < var3; ++var4) {StackTraceElement stackTraceElement = var2[var4];if ("main".equals(stackTraceElement.getMethodName())) {return Class.forName(stackTraceElement.getClassName());}}} catch (ClassNotFoundException var6) {}return null;}
这篇关于[Spring源码] 浅析 SpringApplication`的构造方法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!