[Frame source code] SpringBoot core source code interpretation of startup class source code analysis

insert image description here

First of all, we have to bring our doubts, how does spring boot start the application? To analyze the startup source code of SpringBoot.

When we create a new SpringBoot project, the core method is the run method of the main class.

SpringApplication.run(ArchWebApplication.class, args)

insert image description here

We click the run method to enter the source code, which passes in a class object of the main class of our current program and the main program parameters.

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

Going down, call your own run method, here you start to initialize the SpringApplication object, and then call the run method. When initializing the SpringApplication object, the class object of the main class is also passed in, and then the run method is called to pass in the parameters passed in by the main program.

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

Let's first look at what operations are done when the SpringApplication object is initialized.

Also call its own double-parameter construction method, and null is the incoming resource loader.

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

Going down to the core logic of initializing 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();
	}

The above is an overall logic for creating SpringApplication, so let's take a closer look at the logic WebApplicationType.deduceFromClasspath()inside . WebApplicationType itself is an enumeration class.

  //一共三种方式返回服务条件的一种	
	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()The main function of this method is to judge whether the corresponding class exists through reflection.

	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, after the analysis WebApplicationType.deduceFromClasspath(), let's take a look at getSpringFactoriesInstances()the core logic of this method.

	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;
	}

Let's take a look at loadFactoryNameswhat has been done. The core of it is to load the configuration file and reflect the instantiated object

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);
		}
	}

insert image description here
insert image description here

OK, the initialization of SpringApplication has been completed here, so let's run()do some operations in the following methods. The parameters passed in are the main program.

insert image description here

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;
	}

insert image description here

Ok, let's breakpoint debug the source code execution of the springboot startup class.

Start the SpringBoot application.
insert image description hereinsert image description hereinsert image description hereinsert image description here
insert image description here

Followed by the sorting instance, return the instance.

insert image description here
insert image description here

The rest is the processes we drew above.

insert image description hereinsert image description hereinsert image description here
insert image description here

Ok, so far the whole process of SpringBoot startup has been completed, and finally I will summarize the general process.

  • Initialize SpringApplication and run the run method of SpringApplication
  • Multiple initializers and listeners that read spring.factories
  • Configuration project environment variables, jvm configuration information, configuration file information
  • Pre-initialize the environment, create an environment object
  • Create a Spring container object (ApplicationContext)
  • Call spring's refresh to load the IOC container, automatically configure classes, and create beans and other information
  • Call many listeners and pass the context object
  • Run the relevant runner

Guess you like

Origin blog.csdn.net/weixin_47533244/article/details/130741004