【框架源码】SpringBoot核心源码解读之启动类源码分析

这篇具有很好参考价值的文章主要介绍了【框架源码】SpringBoot核心源码解读之启动类源码分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【框架源码】SpringBoot核心源码解读之启动类源码分析

首先我们要先带着我们的疑问,spring boot是如何启动应用程序?去分析SpringBoot的启动源码。

我们在新建SpringBoot项目时,核心方法就是主类的run方法。

SpringApplication.run(ArchWebApplication.class, args)

【框架源码】SpringBoot核心源码解读之启动类源码分析

我们点击run方法进入到源码中,这块传入的了一个我们当前程序主类的类对象以及主程序参数。

	public static ConfigurableApplicationContext run(Class<?> primarySource,
			String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}

再往下走,调用本身的run方法,这里就开始初始化SpringApplication对象啦,然后在调用run方法。初始化SpringApplication对象时也是将主类的类对象传入进去,然后调用run方法,将主程序传进来的参数传进去。

	public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

我们先来看SpringApplication对象初始化时都做了哪些操作。

同样调用自身的双参构造方法,null为传入的资源加载器。

	public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}

再往下走就到了初始化SpringApplication的核心逻辑啦。

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    //首先是将传来的资源加载器进行赋值,当然我们知道这个资源加载器是null
		this.resourceLoader = resourceLoader;
    //然后在进行类对象的判空
		Assert.notNull(primarySources, "PrimarySources must not be null");
    //然后将传进来的类对像的数组转成list在转成set。
    //(我估计这里是为了去重类对象,因为可以穿进来的可变参数有重复的,可变参数实质就是一个数组)。
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //deduceFromClasspath 方法的目的是用来判断应用是servlet还是reactive应用
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //这一步的逻辑是从spring.factories文件中读取 key为ApplicationContextInitializer的类信息,采用反射实例化对象
    //设置上下文信息
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
    //这一步的逻辑同上,也是从spring.factories文件中读取 key为ApplicationListener的类信息,采用反射实例化对象
  	//设置监听器
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //判断是否为main函数,配置主函数启动类 class信息
		this.mainApplicationClass = deduceMainApplicationClass();
	}

上面是一个创建SpringApplication的整体逻辑,那么我们在具体看一下 WebApplicationType.deduceFromClasspath()里面的逻辑是怎么样的。WebApplicationType本身是一个枚举类。

  //一共三种方式返回服务条件的一种	
	static WebApplicationType deduceFromClasspath() {
    //判断当前应用是不是 REACTIVE应用
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
    //判断是不是 非web应用
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
    //都不是的话返回 web应用方式
		return WebApplicationType.SERVLET;
	}

ClassUtils.isPresent()这个方法的主要作用是通过反射判断相应的类存不存在。

	public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
		try {
			forName(className, classLoader);
			return true;
		}
		catch (IllegalAccessError err) {
			throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
					className + "]: " + err.getMessage(), err);
		}
		catch (Throwable ex) {
			// Typically ClassNotFoundException or NoClassDefFoundError...
			return false;
		}
	}

ok,分析完WebApplicationType.deduceFromClasspath(),我们在来看一下getSpringFactoriesInstances()这个方法的核心逻辑。

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
    //获取ClassLoader
		ClassLoader classLoader = getClassLoader();
		//这一步很重要,重点在于内部是从spring.factories中获取对应的类信息
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //等到需要加载的类信息之后,通过反射创建对象。
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
    //排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

我们来看一下,loadFactoryNames中都干了什么,它这里面的核心就在于加载配置文件,反射实例化对象

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
	}

	//核心逻辑在这个方法
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		//首先先去判断下Map中是否有值,有值的话,就直接返回,相当于一个本地缓存。
    MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
      //如果Map没有值的话,获取资源目录下的spring.factories文件。加载配置
      //源码中 FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
      //下面就是遍历放进map中
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryClassName = ((String) entry.getKey()).trim();
					for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

【框架源码】SpringBoot核心源码解读之启动类源码分析
【框架源码】SpringBoot核心源码解读之启动类源码分析

OK,这里关于SpringApplication初始化的操作就已经完成啦,那么下面我们在一下run()方法里都做些什么操作。传进去的是主程序的参数。

【框架源码】SpringBoot核心源码解读之启动类源码分析

public ConfigurableApplicationContext run(String... args) {
  	//创建StopWatch对象,用于记录服务启动的时间
		StopWatch stopWatch = new StopWatch();
  	//记录服务启动开始时间
		stopWatch.start();
  	//定义应用程序上下文
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
  	//配置运行程序的系统环境,以确保可正确的运行。
		configureHeadlessProperty();
  	//获取在SpringApplication上的所有监听器。
		SpringApplicationRunListeners listeners = getRunListeners(args);
  	//通知所有监听器,启动应用程序
		listeners.starting();
		try {
      //封装应用程序的主程序参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
      //准备应用环境,生成环境变量
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
      //打印应用程序的banner
			Banner printedBanner = printBanner(environment);
      //创建应用上下文对象
			context = createApplicationContext();
      //从spring.factories中获取SpringBootExceptionReporter类型的异常解析器。
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
      //用已有的数据准备上下文,为刷新做准备
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
      //启动应用程序上下文,通过refresh实现
			refreshContext(context);
      //上下文刷新后执行一些后置处理
			afterRefresh(context, applicationArguments);
      //记录结束时间
			stopWatch.stop();
      //判断是否需要记录应用程序的启动信息
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
      //通知所有监听器,应用程序已经启动,传递上下文对象和启动时间
			listeners.started(context);
      //运行所有已经注册的runner
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
      //发布应用上下文就绪事件
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

【框架源码】SpringBoot核心源码解读之启动类源码分析

ok,下面我们来断点调试下springboot启动类的源码执行。

启动SpringBoot应用程序。
【框架源码】SpringBoot核心源码解读之启动类源码分析【框架源码】SpringBoot核心源码解读之启动类源码分析【框架源码】SpringBoot核心源码解读之启动类源码分析【框架源码】SpringBoot核心源码解读之启动类源码分析
【框架源码】SpringBoot核心源码解读之启动类源码分析

后面就是排序实例 ,返回实例。

【框架源码】SpringBoot核心源码解读之启动类源码分析
【框架源码】SpringBoot核心源码解读之启动类源码分析

剩下的就是我们上面画的那些流程啦。

【框架源码】SpringBoot核心源码解读之启动类源码分析【框架源码】SpringBoot核心源码解读之启动类源码分析【框架源码】SpringBoot核心源码解读之启动类源码分析
【框架源码】SpringBoot核心源码解读之启动类源码分析

ok,至此SpringBoot启动的全流程就已经完成啦,最后在总结一下大体的流程。文章来源地址https://www.toymoban.com/news/detail-463866.html

  • 初始化SpringApplication,运行SpringApplication的run方法
  • 读取 spring.factories 的多个初始化器和监听器
  • 配置项目中环境变量、jvm配置信息、配置文件信息
  • 预初始化环境,创建环境对象
  • 创建Spring容器对象(ApplicationContext)
  • 调用spring的refresh加载IOC容器、自动配置类,并创建bean等信息
  • 调用很多监听器并传递上下文对象
  • 运行相关runner

到了这里,关于【框架源码】SpringBoot核心源码解读之启动类源码分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringBoot的启动流程源码分析

    new 一个IOC容器,传入配置好的文件xml,在这个地方打bug 在这个debug的栈帧中,下面几个不用看,直接看到getBean 内容如图所示,name传的就是我们在xml的bean标签的id,这里是instanceA 进入到doGetBean后,因为我是从IOC初始化容器debug进来的所以第一次通过。 Object sharedInstance = getSingl

    2024年01月24日
    浏览(34)
  • SpringBoot源码学习4——SpringBoot内嵌Tomcat启动流程源码分析

    系列文章目录和关于我 我在初学spring的时候,很懵逼,因为整个项目中不存在main方法,让我有点摸不着头脑。那时候我知道有个东西叫tomcat是它监听了端口,解析了协议调到了我的servlet。 在我初学SpringBoot的时候,很懵逼,有main方法了,但是tomcat在哪里呢,又是如何启动起

    2024年02月04日
    浏览(32)
  • SpringBoot3.X源码分析(启动流程)

    1 启动入口 静态辅助类,可用于运行使用默认配置(即我们添加的一系列注解)的指定源的 SpringApplication 。 primarySource - 要载入的主要源,即指定源,这里为传入的 Application.class  Class? :泛型决定了任何类都可以传入 args - 应用程序参数(通常从main方法传递) 返回:正在运

    2024年01月23日
    浏览(29)
  • SpringBoot配置外部Tomcat项目启动流程源码分析

    SpringBoot应用默认以Jar包方式并且使用内置Servlet容器(默认Tomcat),该种方式虽然简单但是默认不支持JSP并且优化容器比较复杂。故而我们可以使用习惯的外置Tomcat方式并将项目打War包。 ① 同样使用Spring Initializer方式创建项目 ② 打包方式选择\\\"war\\\" ③ 选择添加的模块 ④ 创建的

    2024年02月04日
    浏览(30)
  • SpringBoot源码分析之Tomcat是如何在SpringBoot中启动的?

    一.前言 我们知道SpringBoot可以直接把传统的war包打成可执行的jar包,直接启动。这得益于SpringBoot内置了容器可以直接启动。本文将以 Tomcat 为例,来看看 SpringBoot 是如何启动 Tomcat 的。 一.SpringApplication初始化 调用到最终的run方法我们来看一下 这里面首先创建了一个SpringAppl

    2024年02月05日
    浏览(29)
  • SpringBoot-Run启动流程(源码分析)—看不懂来揍我

    目录 前言 Run()方法 1、实例化SpringApplication对象 1、加载容器 2、装配初始化器 3、装配监听器  4、加载主类 2、执行Run()方法 1、设置headless 2、启用SpringApplicationListener 3、加载Banner 1、图片Banner 2、文本Banner 4、异常报告类加载 5、准备上下文         6、刷新上下文 7、系统

    2024年02月14日
    浏览(27)
  • PaddleSeg分割框架解读[01] 核心设计解析

    特别注意,这块具体实现的类,如class Cityscapes(Dataset)等,称为组件; 组件管理器,则为相应的模型model管理器、数据集datasets管理器等。

    2024年02月20日
    浏览(31)
  • Springboot 核心注解和基本配置解读

    目录   1. Springboot 入门与原理 1.1 Springboot 简介 1.1.1 什么是Springboot 1.1.2 Springboot 主要优点 1.2 Springboot 相关注解 1.2.1 元注解  1.2.1.1 @Target 1.2.1.2 @Retention 1.2.2 @Configuration 1.2.3 @Import 1.2.3.1 直接注入 1.2.3.2 实现 ImportSelector 注入 1.2.3.3 实现 ImportBeanDefinitionRegistrar 接口 注入 1.2.4 @

    2024年02月09日
    浏览(35)
  • SpringSecurity源码分析(一) SpringBoot集成SpringSecurity即Spring安全框架的加载过程

          Spring Security是一个强大的并且高度可定制化的访问控制框架。 它基于spring应用。 Spring Security是聚焦于为java应用提供授权和验证的框架。像所有的spring项目一样,Spring Security真正的强大在于可以非常简单的拓展功能来实现自定义的需求。       在分析SpringBoot集成的Sp

    2024年02月03日
    浏览(35)
  • CVPR 2023 | 主干网络FasterNet 核心解读 代码分析

    本文分享来自CVPR 2023的论文,提出了一种 快速的主干网络,名为FasterNet 。 论文提出了 一种新的卷积算子,partial convolution,部分卷积(PConv) ,通过 减少冗余计算 和 内存访问 来更有效地提取空间特征。 创新在于部分卷积(PConv), 它选择一部分通道的特性进行常规卷积 , 剩余

    2024年02月06日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包