Spring Boot-源码阅读-启动主流程(一)
说明
待定
main方法
通过以下方法就完成了自定义启动和容器初始化是怎么完成的呢
@SpringBootApplication public class FinancialAnalysisApplication { public static void main(String[] args) {
//<1>run启动 SpringApplication.run(FinancialAnalysisApplication.class, args); } }
<1>
org.springframework.boot.SpringApplication#run
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { //包装成数组类型 我们表示我们可以配置多个 return run(new Class<?>[] { primarySource }, args); }
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { //<2>我们先看构造函数初始化做了什么 <8>run启动 return new SpringApplication(primarySources).run(args); }
<2>
@SuppressWarnings({ "unchecked", "rawtypes" }) public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); //我们的启动类指定的class 如: SpringApplication.run(FinancialAnalysisApplication.class, args); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //<3>获得环境类型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); //----------------------------<4>getSpringFactoriesInstances方法是在spring.factories找到实现了指定类型的类--------------------------- /** * 获取BootstrapRegistryInitializer的实现类 默认没有实现 */ this.bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); /** * ApplicationContextInitializer 看spring-boot.jar包下的的默认配置有 * org.springframework.context.ApplicationContextInitializer=\ * org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ * org.springframework.boot.context.ContextIdApplicationContextInitializer,\ * org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ * org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\ * org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer */ setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); /** * ApplicationListener 看spring-boot.jar包下的的默认配置 后续由SpringApplicationRunListeners在各个时机委托给EVentPublishingRunListener 发送对应的Event事件 由以下监听器监听 * # Application Listeners * org.springframework.context.ApplicationListener=\ * org.springframework.boot.ClearCachesApplicationListener,\ * org.springframework.boot.builder.ParentContextCloserApplicationListener,\ * org.springframework.boot.context.FileEncodingApplicationListener,\ * org.springframework.boot.context.config.AnsiOutputApplicationListener,\ * org.springframework.boot.context.config.DelegatingApplicationListener,\ * org.springframework.boot.context.logging.LoggingApplicationListener,\ * org.springframework.boot.env.EnvironmentPostProcessorApplicationListener */ setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //<7>这里主要是获取main方法进入的类型 this.mainApplicationClass = deduceMainApplicationClass(); }
<3>
org.springframework.boot.WebApplicationType#deduceFromClasspath
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler"; private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet"; private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer"; private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; static WebApplicationType deduceFromClasspath() { //如果可以加载DispatcherHandler 同时WEBMVC_INDICATOR_CLASS JERSEY_INDICATOR_CLASS不存在则类型是REACTIVE if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { //REACTIVE则是一种比较新的非阻塞的web框架,对应spring-webflux,需要启动支持reactive的web容器 return WebApplicationType.REACTIVE; } //如果SERVLET_INDICATOR_CLASSES 都无法加载则是NoNE for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { //NONE就是什么都没有,按照正常的代码走即可不需要额外启动web容器如tomcat等 return WebApplicationType.NONE; } } //SERVLET则代表这是一个传统的servlet的web程序,对应SpringMVC 默认是SERVLET return WebApplicationType.SERVLET; }
<4>
org.springframework.boot.SpringApplication#getSpringFactoriesInstances
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); }
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { //获得当前类的类加载器 ClassLoader classLoader = getClassLoader(); // <5>从spring.factories获取对应类型配置类的全名称 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); //反射创建 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); //如果打了@order注解 则根据@Order注解优先级排序返回 AnnotationAwareOrderComparator.sort(instances); return instances; }
<5>
org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { //获取查找类型的名字后续通过他为key查找对应的类型 String factoryTypeName = factoryType.getName(); //<6>触发加载根据类型封装成map 然后通过factoryType去map获取对应的实现类配置的名字 return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); }
<6>
org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { //先确定当前ClassLoader是否加载了配置 防止重复加载如果加载了直接返回加载后的map Map<String, List<String>> result = cache.get(classLoader); if (result != null) { return result; } result = new HashMap<>(); try { //获得所有META-INF/spring.factories下的配置 Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); //遍历加载各个url下的文件 while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); //因为是properties格式配置所以转换为properties /** * 如: * org.springframework.boot.SpringApplicationRunListener=\ * org.springframework.boot.context.event.EventPublishingRunListener */ Properties properties = PropertiesLoaderUtils.loadProperties(resource); //遍历封装到map for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); for (String factoryImplementationName : factoryImplementationNames) { result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()) .add(factoryImplementationName.trim()); } } } // 去重 result.replaceAll((factoryType, implementations) -> implementations.stream().distinct() .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))); //封装到cache 后续获取直接从cache获取 cache.put(classLoader, result); } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } return result; }
<7>
org.springframework.boot.SpringApplication#deduceMainApplicationClass
private Class<?> deduceMainApplicationClass() { try { //获得当前执行堆栈 StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); //遍历知道获取main方法的入口堆栈 并获取类的class for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
<8>
org.springframework.boot.SpringApplication#run
public ConfigurableApplicationContext run(String... args) { //启动开始时间 long startTime = System.nanoTime(); //<9>创建DefaultBootstrapContext DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; //设置java.awt.headless配置 configureHeadlessProperty(); //<10>获得监听器 SpringApplicationRunListeners listeners = getRunListeners(args);
//调用starting 发送ApplicationStartingEvent事件 starting事件 listeners.starting(bootstrapContext, this.mainApplicationClass); try { //封装系统变量 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //<11>加载环境变量 ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); //spring.beaninfo.ignore默认配置为true configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); //<13>通过ApplicationContextFactory创建Spring容器 context = createApplicationContext(); //容器状态设置 context.setApplicationStartup(this.applicationStartup); //<14>容器初始化前的相关配置 prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); //<16>调用容器的refresh方法 进入spring生命周期 refreshContext(context); //钩子方法 子类可以自定义实现 afterRefresh(context, applicationArguments); Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup); } //发送listeners的ApplicationStartedEvent事件 listeners.started(context, timeTakenToStartup); //<15>从容器中获得ApplicationRunner 和CommandLineRunner 完成回调 callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); listeners.ready(context, timeTakenToReady); } catch (Throwable ex) { handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } return context; }
<9>
org.springframework.boot.SpringApplication#createApplicationContext
private DefaultBootstrapContext createBootstrapContext() { DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); //调用bootstrapRegistryInitializers我们可以做初始化配置 this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext)); return bootstrapContext; }
<10>
org.springframework.boot.SpringApplication#getRunListeners
rivate SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; //根据getSpringFactoriesInstances 获取实现SpringApplicationRunListener接口 默认是EventpublishingRunListener 通过SpringApplicationRunListeners包装 /** * # Run Listeners * org.springframework.boot.SpringApplicationRunListener=\ * org.springframework.boot.context.event.EventPublishingRunListener 默认 可以持有当前application的引用 所以可以拿到listeners
* 发送时间则是由spring.factories applicationContextListener监听 */ return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup); }
<11>
org.springframework.boot.SpringApplication#prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // <12>根据环境初始化environment ConfigurableEnvironment environment = getOrCreateEnvironment(); //将系统变量设置到environment configureEnvironment(environment, applicationArguments.getSourceArgs());
//<17>这里主要是将configurationProperties替换放到第一位 优先级最高 ConfigurationPropertySources.attach(environment); //发送listener ApplicationEnviromentPreparedEvent 对应消费将触发我们application.yml配置文件加载 /** * # Run Listeners * org.springframework.boot.SpringApplicationRunListener=\ * org.springframework.boot.context.event.EventPublishingRunListener 默认 */ listeners.environmentPrepared(bootstrapContext, environment); DefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties."); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = convertEnvironment(environment); } ConfigurationPropertySources.attach(environment); return environment; }
<12>
org.springframework.boot.SpringApplication#getOrCreateEnvironment
private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } switch (this.webApplicationType) { case SERVLET: return new ApplicationServletEnvironment(); case REACTIVE: return new ApplicationReactiveWebEnvironment(); default: return new ApplicationEnvironment(); } }
<13>
com.biaoguoworks.sac.chains.commons.controller.ChainBaseController#createApplicationContext
private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT; protected ConfigurableApplicationContext createApplicationContext() { return this.applicationContextFactory.create(this.webApplicationType); } ApplicationContextFactory DEFAULT = (webApplicationType) -> { try { switch (webApplicationType) { case SERVLET: return new AnnotationConfigServletWebServerApplicationContext(); case REACTIVE: return new AnnotationConfigReactiveWebServerApplicationContext(); default: return new AnnotationConfigApplicationContext(); } } catch (Exception ex) { throw new IllegalStateException("Unable create a default ApplicationContext instance, " + "you may need a custom ApplicationContextFactory", ex); } };
<14>
org.springframework.boot.SpringApplication#prepareContext
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { //设置环境变量 context.setEnvironment(environment);
//针对BeanNameGenerator resourceLoader、ConversionService设置缺省值 postProcessApplicationContext(context); //触发ApplicationContextInitializer回调 也是从spring.factories获取 applyInitializers(context); //发送ApplicationContextInitializedEvent事件 listeners.contextPrepared(context); bootstrapContext.close(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 AbstractAutowireCapableBeanFactory) { ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences); 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");
//source就是我们的启动类,这里主要是将我们的启动类的BeanDefintion加载到BeanFacotry,所以我们再启动类上打相关注解有效 load(context, sources.toArray(new Object[0])); //发送ApplicationPreparedEvent事件 listeners.contextLoaded(context); }
<15>
org.springframework.boot.SpringApplication#callRunners
private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<>(); //从容器中获得ApplicationRunner实现 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); } } }
<16>
/** * 优雅关闭 * @param context */ static final SpringApplicationShutdownHook shutdownHook = new SpringApplicationShutdownHook(); private void refreshContext(ConfigurableApplicationContext context) { if (this.registerShutdownHook) { shutdownHook.registerApplicationContext(context); } refresh(context); }
<17>
private static final String ATTACHED_PROPERTY_SOURCE_NAME = "configurationProperties"; public static void attach(Environment environment) { Assert.isInstanceOf(ConfigurableEnvironment.class, environment); //这只是一个门面 内部数组管理多个propertiesSource MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources(); //获取configurationProperties PropertySource<?> attached = getAttached(sources); //如果存移除然后插入到头部 if (attached != null && attached.getSource() != sources) { //移除 sources.remove(ATTACHED_PROPERTY_SOURCE_NAME); attached = null; } //重新放入头部通过ConfigurationPropertySourcesPropertySource 包装 这个内部管理多个SpringConfigurationPropertySources 同时也是根据SpringConfigurationPropertySources包装了一层 if (attached == null) { sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME, new SpringConfigurationPropertySources(sources))); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
2020-02-25 SpringMVC源码阅读-通过画图理解初始化(十一)
2019-02-25 elasticsearch-分析器(五)
2019-02-25 elasticsearch-搜索-基本搜索(四)