国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Spring Boot——Spring Boot啟動(dòng)原理

這篇具有很好參考價(jià)值的文章主要介紹了Spring Boot——Spring Boot啟動(dòng)原理。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

系列文章目錄



一、Spring Boot啟動(dòng)的宏觀流程圖

Spring Boot——Spring Boot啟動(dòng)原理,《Spring boot》,spring boot,后端,java

二、Spring Boot啟動(dòng)流程

2.1 初始化new SpringApplication

2.1.1Spring Boot入口

@SpringBootApplication
public class SampleWebJspApplication extends SpringBootServletInitializer {
	public static void main(String[] args) {
		SpringApplication.run(SampleWebJspApplication.class, args);
	}
}

2.1.2初始化SpringApplication

準(zhǔn)備階段,在程序運(yùn)行之前初始化一些屬性,用于在后序啟動(dòng)應(yīng)用程序過程中。

//創(chuàng)建 SpringApplication 實(shí)例時(shí),初始化各種重要的屬性,包括應(yīng)用程序的主要來源、Web 應(yīng)用類型、初始化器和監(jiān)聽器等
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		//根據(jù)類路徑來推斷 Spring Boot 應(yīng)用程序的 Web 應(yīng)用類型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		//獲取所有實(shí)現(xiàn)了 ApplicationContextInitializer 接口的實(shí)例,并將它們?cè)O(shè)置為應(yīng)用程序上下文的初始化器(initializers)。
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		//獲取所有實(shí)現(xiàn)了 ApplicationListener 接口的實(shí)例,并將它們?cè)O(shè)置為應(yīng)用程序的監(jiān)聽器(listeners)
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		//4.設(shè)置應(yīng)用程序運(yùn)行主類
		this.mainApplicationClass = deduceMainApplicationClass();
	}
2.1.2.1判斷當(dāng)前應(yīng)用程序類型
//根據(jù)類路徑來推斷 Spring Boot 應(yīng)用程序的 Web 應(yīng)用類型
this.webApplicationType = WebApplicationType.deduceFromClasspath();

//根據(jù)類路徑中存在的特定類來推斷 Spring Boot 應(yīng)用程序的 Web 應(yīng)用類型
static WebApplicationType deduceFromClasspath() {
		//檢查類路徑中是否存在 WEBFLUX_INDICATOR_CLASS 并且不存在 WEBMVC_INDICATOR_CLASS 和 JERSEY_INDICATOR_CLASS。
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			//即響應(yīng)式 Web 應(yīng)用程序
			return WebApplicationType.REACTIVE;
		}
		//檢查類路徑中是否存在當(dāng)前遍歷的類名。
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				//表示非 Web 應(yīng)用程序。
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

2.1.2.2設(shè)置應(yīng)用程序的所有初始化器(initializers)
//獲取所有實(shí)現(xiàn)了 ApplicationContextInitializer 接口的實(shí)例,并將它們?cè)O(shè)置為應(yīng)用程序上下文的初始化器(initializers)。
setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}

//通過加載 "spring.factories" 配置文件中指定類型的工廠名稱,并創(chuàng)建對(duì)應(yīng)的工廠實(shí)例,然后根據(jù)工廠實(shí)例的排序順序返回一個(gè)排序后的工廠實(shí)例的集合。
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		// 從 "spring.factories" 配置文件中加載指定類型的工廠名稱,并將它們放入一個(gè)有序的集合中
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		//通過工廠名稱創(chuàng)建指定類型的工廠實(shí)例,并將它們放入一個(gè)列表中。
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		//對(duì)工廠實(shí)例列表進(jìn)行排序。
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}			

上面這段代碼主要是通過加載 “spring.factories” 配置文件中指定類型的工廠名稱,并創(chuàng)建對(duì)應(yīng)的工廠實(shí)例,然后根據(jù)工廠實(shí)例的排序順序返回一個(gè)排序后的工廠實(shí)例的集合。關(guān)鍵方法是SpringFactoriesLoader.loadFactoryNames()方法。

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        //只會(huì)返回key為org.springframework.context.ApplicationContextInitializer的value
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                MultiValueMap<String, String> result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Map.Entry<?, ?> entry = (Map.Entry)var6.next();
                        String factoryClassName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryName = var9[var11];
                            result.add(factoryClassName, factoryName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }

loadFactoryNames()該方法會(huì)從類路徑的 META-INF/spring.factories 讀取相應(yīng)配置文件,讀取配置文件中key為org.springframework.context.ApplicationContextInitializer對(duì)應(yīng)的value。例如,spring-boot這個(gè)jar包它的 META-INF/spring.factories 部分定義為:
Spring Boot——Spring Boot啟動(dòng)原理,《Spring boot》,spring boot,后端,java

獲取到全類名之后,之后會(huì)進(jìn)行實(shí)例化,并返回實(shí)例。如下代碼:

//通過工廠名稱創(chuàng)建指定類型的工廠實(shí)例,并將它們放入一個(gè)列表中。
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);


private <T> List<T> createSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
			Set<String> names) {
		List<T> instances = new ArrayList<>(names.size());
		for (String name : names) {
			try {
				Class<?> instanceClass = ClassUtils.forName(name, classLoader);
				Assert.isAssignable(type, instanceClass);
				Constructor<?> constructor = instanceClass
						.getDeclaredConstructor(parameterTypes);
				T instance = (T) BeanUtils.instantiateClass(constructor, args);
				instances.add(instance);
			}
			catch (Throwable ex) {
				throw new IllegalArgumentException(
						"Cannot instantiate " + type + " : " + name, ex);
			}
		}
		return instances;
	}
2.1.2.3設(shè)置應(yīng)用程序的所有監(jiān)聽器(listeners)

這只應(yīng)用程序的listeners與設(shè)置初始化器(initializers)的方式相同,會(huì)從類路徑的 META-INF/spring.factories 讀取相應(yīng)配置文件,讀取配置文件中key為org.springframework.context.ApplicationListener對(duì)應(yīng)的value。再根據(jù)拿到的全類名進(jìn)行實(shí)例化,最后完成設(shè)置應(yīng)用程序的所有監(jiān)聽器。

2.1.2.4設(shè)置應(yīng)用程序運(yùn)行主類
//4.設(shè)置應(yīng)用程序運(yùn)行主類
		this.mainApplicationClass = deduceMainApplicationClass();

private Class<?> deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}


到這里SpringApplication實(shí)例的初始化和一些準(zhǔn)備工作就結(jié)束了。

2.2執(zhí)行run方法

宏觀上run方法都做了哪些工作:

public ConfigurableApplicationContext run(String... args) {
		//1.這里首先創(chuàng)建了一個(gè) StopWatch 對(duì)象用于計(jì)時(shí)監(jiān)控整個(gè)應(yīng)用程序的啟動(dòng)時(shí)間。
		StopWatch stopWatch = new StopWatch();
		//啟動(dòng)計(jì)時(shí)監(jiān)控
		stopWatch.start();
		//2.初始化應(yīng)用上下文
		ConfigurableApplicationContext context = null;
		//初始化異常報(bào)告
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		//3.配置 Headless 屬性:Headless 模式用于在沒有顯示器、鍵盤或鼠標(biāo)的環(huán)境中運(yùn)行應(yīng)用程序。
		// 這里設(shè)置了 Headless 屬性,以便在沒有 GUI 的環(huán)境中正確啟動(dòng)應(yīng)用程序。
		configureHeadlessProperty();
		//4.獲取 SpringApplicationRunListeners:這一步是為了獲取所有注冊(cè)的 SpringApplicationRunListener 監(jiān)聽器。
		// 這些監(jiān)聽器在應(yīng)用程序的不同生命周期階段會(huì)被通知,從而能夠?qū)?yīng)用程序進(jìn)行擴(kuò)展和自定義處理。
		SpringApplicationRunListeners listeners = getRunListeners(args);
		//通知監(jiān)聽者,開始啟動(dòng)
		// 這里通過調(diào)用 listeners.starting() 來通知所有的 SpringApplicationRunListener 監(jiān)聽器,應(yīng)用程序即將開始啟動(dòng)。
		listeners.starting();
		try {
			//5.獲取并解析應(yīng)用程序在命令行上傳入的參數(shù)。
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			/*
			6.根據(jù)SpringApplicationRunListeners以及參數(shù)來準(zhǔn)備環(huán)境:Spring 環(huán)境包含了應(yīng)用程序的配置信息,
			它由多個(gè)屬性源組成,包括默認(rèn)屬性、命令行參數(shù)、系統(tǒng)屬性、配置文件等。
			 */
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			//7.配置忽略 BeanInfo:這是一個(gè)特定的處理,用于在某些環(huán)境中忽略對(duì) JavaBean 的分析。
			configureIgnoreBeanInfo(environment);
			//8.打印 Banner:Banner 是在應(yīng)用程序啟動(dòng)時(shí)打印的一個(gè) ASCII 藝術(shù)字或自定義標(biāo)志。
			Banner printedBanner = printBanner(environment);
			//9.創(chuàng)建Spring上下文,其中包含了所有的 Spring Bean 和配置。
			context = createApplicationContext();
			//10.獲取所有注冊(cè)的 SpringBootExceptionReporter 實(shí)現(xiàn)類的實(shí)例。
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			//11.Spring上下文前置處理:包括設(shè)置環(huán)境、添加 BeanPostProcessor、注冊(cè) BeanDefinition 等。
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			//12.Spring上下文刷新:這將觸發(fā) Spring Bean 的實(shí)例化和依賴注入等過程。
			refreshContext(context);
			//13.spring上下文后置處理:可以在這里執(zhí)行額外的初始化操作。
			afterRefresh(context, applicationArguments);
			//14.停止計(jì)時(shí)器
			stopWatch.stop();
			//15.如果設(shè)置了 logStartupInfo,則會(huì)記錄應(yīng)用程序啟動(dòng)信息,包括啟動(dòng)時(shí)間和主類等。
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			//16.調(diào)用 listeners.running(context) 來通知所有監(jiān)聽者,應(yīng)用程序正在運(yùn)行。
			listeners.started(context);
			//17.執(zhí)行所有注冊(cè)的 ApplicationRunner 和 CommandLineRunner,這些是在應(yīng)用程序啟動(dòng)完成后自動(dòng)執(zhí)行的任務(wù)。
			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);
		}
		//18.返回上下文對(duì)象
		return context;
	}

下面將會(huì)詳細(xì)的介紹各個(gè)環(huán)節(jié):

2.2.1開啟計(jì)時(shí)器

//1.這里首先創(chuàng)建了一個(gè) StopWatch 對(duì)象用于計(jì)時(shí)監(jiān)控整個(gè)應(yīng)用程序的啟動(dòng)時(shí)間。
		StopWatch stopWatch = new StopWatch();
		//啟動(dòng)計(jì)時(shí)監(jiān)控
		stopWatch.start();

//start 方法時(shí),它會(huì)開始記錄給定任務(wù)的運(yùn)行時(shí)間,確保不會(huì)重復(fù)計(jì)時(shí)同一個(gè)任務(wù)。如果你嘗試在計(jì)時(shí)器運(yùn)行時(shí)再次調(diào)用 
//start 方法,它將會(huì)拋出異常,防止重復(fù)啟動(dòng)計(jì)時(shí)器。
public void start(String taskName) throws IllegalStateException {
    if (this.currentTaskName != null) {
        throw new IllegalStateException("Can't start StopWatch: it's already running");
    } else {
        this.currentTaskName = taskName;
        this.startTimeMillis = System.currentTimeMillis();
    }
}

2.2.2初始化應(yīng)用上下文和初始化異常報(bào)告

//2.初始化應(yīng)用上下文
		ConfigurableApplicationContext context = null;
		//初始化異常報(bào)告
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

2.2.3設(shè)置Headless屬性

//配置 Headless 屬性:Headless 模式用于在沒有顯示器、鍵盤或鼠標(biāo)的環(huán)境中運(yùn)行應(yīng)用程序。
		// 這里設(shè)置了 Headless 屬性,以便在沒有 GUI 的環(huán)境中正確啟動(dòng)應(yīng)用程序。
		configureHeadlessProperty();

/*
 * @description:這段代碼用于配置 Java AWT 的 Headless 屬性。Headless 模式是在沒有顯示器、鍵盤或鼠標(biāo)的環(huán)境中運(yùn)行 Java 應(yīng)用程序,
 * 通常在服務(wù)器上或無界面的操作系統(tǒng)中使用。當(dāng)應(yīng)用程序在 Headless 模式下運(yùn)行時(shí),Java AWT 將不會(huì)初始化圖形界面,以減少資源消耗。
 * @author: wangwei
 * @date: 2023/7/26 19:39
 * @param: []
 * @return: void
 **/
private void configureHeadlessProperty() {
   System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
         System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

2.2.4獲取所有注冊(cè)的listeners并發(fā)布ApplicationStartingEvent事件。

這里依然是使用了getSpringFactoriesInstances()方法來獲取實(shí)例。
還是從 META-INF/spring.factories 中讀取Key為 org.springframework.boot.SpringApplicationRunListener 的Values,最后根據(jù)全類名進(jìn)行實(shí)例化。文章來源地址http://www.zghlxwxcb.cn/news/detail-612641.html

//獲取 SpringApplicationRunListeners:這一步是為了獲取所有注冊(cè)的 SpringApplicationRunListener 監(jiān)聽器。
		// 這些監(jiān)聽器在應(yīng)用程序的不同生命周期階段會(huì)被通知,從而能夠?qū)?yīng)用程序進(jìn)行擴(kuò)展和自定義處理。
		SpringApplicationRunListeners listeners = getRunListeners(args);


private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}

	//通過加載 "spring.factories" 配置文件中指定類型的工廠名稱,并創(chuàng)建對(duì)應(yīng)的工廠實(shí)例,然后根據(jù)工廠實(shí)例的排序順序返回一個(gè)排序后的工廠實(shí)例的集合。
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		// 從 "spring.factories" 配置文件中加載指定類型的工廠名稱,并將它們放入一個(gè)有序的集合中
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		//通過工廠名稱創(chuàng)建指定類型的工廠實(shí)例,并將它們放入一個(gè)列表中。
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		//對(duì)工廠實(shí)例列表進(jìn)行排序。
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}


最后回到SpringApplication中的run方法中
-------------------------------------------------

		//通知監(jiān)聽者,開始啟動(dòng)
		// 這里通過調(diào)用 listeners.starting() 來通知所有的 SpringApplicationRunListener 監(jiān)聽器,應(yīng)用程序即將開始啟動(dòng)。
		listeners.starting();

public void starting() {
   for (SpringApplicationRunListener listener : this.listeners) {
      listener.starting();
   }
}
@Override
public void starting() {
   this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
//事件傳播機(jī)制的實(shí)現(xiàn)。它用于廣播(multicast)一個(gè)應(yīng)用程序事件給所有對(duì)該事件感興趣的監(jiān)聽器,并支持在多線程環(huán)境中異步執(zhí)行監(jiān)聽器的處理邏輯。
public void multicastEvent(ApplicationEvent event) {
    this.multicastEvent(event, this.resolveDefaultEventType(event));
}

public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
    Executor executor = this.getTaskExecutor();
    Iterator var5 = this.getApplicationListeners(event, type).iterator();

    while(var5.hasNext()) {
        ApplicationListener<?> listener = (ApplicationListener)var5.next();
        if (executor != null) {
            executor.execute(() -> {
                this.invokeListener(listener, event);
            });
        } else {
            this.invokeListener(listener, event);
        }
    }

}


2.2.5獲取并解析應(yīng)用程序在命令行上傳入的參數(shù)

//獲取并解析應(yīng)用程序在命令行上傳入的參數(shù)。
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

public DefaultApplicationArguments(String[] args) {
   Assert.notNull(args, "Args must not be null");
   this.source = new Source(args);
   this.args = args;
}

//個(gè)命令行參數(shù)解析器的實(shí)現(xiàn),用于將傳入的命令行參數(shù)數(shù)組 args 解析成一個(gè) 
//CommandLineArgs 對(duì)象。
//CommandLineArgs 是一個(gè)自定義類,用于存儲(chǔ)解析后的命令行參數(shù)信息。
public CommandLineArgs parse(String... args) {
    CommandLineArgs commandLineArgs = new CommandLineArgs();
    String[] var3 = args;
    int var4 = args.length;

    for(int var5 = 0; var5 < var4; ++var5) {
        String arg = var3[var5];
        if (arg.startsWith("--")) {
            String optionText = arg.substring(2, arg.length());
            String optionValue = null;
            String optionName;
            if (optionText.contains("=")) {
                optionName = optionText.substring(0, optionText.indexOf(61));
                optionValue = optionText.substring(optionText.indexOf(61) + 1, optionText.length());
            } else {
                optionName = optionText;
            }

            if (optionName.isEmpty() || optionValue != null && optionValue.isEmpty()) {
                throw new IllegalArgumentException("Invalid argument syntax: " + arg);
            }

            commandLineArgs.addOptionArg(optionName, optionValue);
        } else {
            commandLineArgs.addNonOptionArg(arg);
        }
    }

    return commandLineArgs;

2.2.6讀取環(huán)境配置信息

/*
根據(jù)SpringApplicationRunListeners以及參數(shù)來準(zhǔn)備環(huán)境:Spring 環(huán)境包含了應(yīng)用程序的配置信息,
它由多個(gè)屬性源組成,包括默認(rèn)屬性、命令行參數(shù)、系統(tǒng)屬性、配置文件等。
*/
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

/*
 * @description:為應(yīng)用程序創(chuàng)建并準(zhǔn)備一個(gè)配置環(huán)境(ConfigurableEnvironment)。它將應(yīng)用程序的命令行參數(shù)、配置屬性等信息設(shè)置到環(huán)境中,
 * 并通知相關(guān)的監(jiān)聽器。通過這個(gè)準(zhǔn)備環(huán)境的過程,應(yīng)用程序可以在后續(xù)的啟動(dòng)過程中正確加載和配置所需的屬性和配置信息。
 * @author: wangwei
 * @date: 2023/7/26 20:14
 * @param: [listeners, applicationArguments]
 * @return: org.springframework.core.env.ConfigurableEnvironment
 **/
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
   // Create and configure the environment
   //獲取或創(chuàng)建應(yīng)用程序的環(huán)境對(duì)象。環(huán)境是一個(gè)配置容器,用于保存應(yīng)用程序的配置信息、屬性和其他環(huán)境相關(guān)的內(nèi)容。
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   //配置環(huán)境,將應(yīng)用程序的命令行參數(shù)(applicationArguments)設(shè)置到環(huán)境中。
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   //將配置屬性源(ConfigurationPropertySource)附加到環(huán)境中,用于加載配置屬性。
   ConfigurationPropertySources.attach(environment);
   //通知監(jiān)聽器,應(yīng)用程序的環(huán)境已準(zhǔn)備好。這是應(yīng)用程序啟動(dòng)過程中通知監(jiān)聽器的一個(gè)生命周期事件。
   listeners.environmentPrepared(environment);
   //將環(huán)境對(duì)象與當(dāng)前的 Spring 應(yīng)用程序?qū)嵗M(jìn)行綁定,以便可以在應(yīng)用程序中訪問該環(huán)境。
   bindToSpringApplication(environment);
   //如果當(dāng)前環(huán)境不是自定義環(huán)境,則執(zhí)行下面的邏輯。
   if (!this.isCustomEnvironment) {
      /*
      創(chuàng)建一個(gè) EnvironmentConverter 對(duì)象,并將現(xiàn)有的環(huán)境對(duì)象 environment 作為參數(shù)傳入。
      調(diào)用 convertEnvironmentIfNecessary 方法,將當(dāng)前環(huán)境轉(zhuǎn)換成指定類型的環(huán)境。這通常是為了適應(yīng)不同的環(huán)境配置。
       */
      environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
            deduceEnvironmentClass());
   }
   //再次將配置屬性源(ConfigurationPropertySource)附加到環(huán)境中,用于加載配置屬性。
   ConfigurationPropertySources.attach(environment);
   return environment;
}



2.2.7設(shè)置忽略BeanInfo

//配置忽略 BeanInfo:這是一個(gè)特定的處理,用于在某些環(huán)境中忽略對(duì) JavaBean 的分析。
			configureIgnoreBeanInfo(environment);

2.2.8.打印Banner

//打印 Banner:Banner 是在應(yīng)用程序啟動(dòng)時(shí)打印的一個(gè) ASCII 藝術(shù)字或自定義標(biāo)志。
			Banner printedBanner = printBanner(environment);

2.2.9創(chuàng)建上下文createApplicationContext

//創(chuàng)建Spring上下文,其中包含了所有的 Spring Bean 和配置。
			context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
		//獲取應(yīng)用程序上下文的類類型。這個(gè)類類型可以在應(yīng)用程序配置中指定,表示要使用的自定義上下文類。
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
					//如果 this.webApplicationType 為 SERVLET,則使用默認(rèn)的 Servlet Web 上下文類
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					//如果 this.webApplicationType 為 SERVLET,則使用默認(rèn)的 Servlet Web 上下文類
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					//對(duì)于其他情況,默認(rèn)使用普通的上下文類類型 DEFAULT_CONTEXT_CLASS。
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
						ex);
			}
		}
		//實(shí)例化 contextClass 對(duì)象,得到一個(gè)應(yīng)用程序上下文的實(shí)例。
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

2.2.10.獲取所有異常報(bào)告器

//獲取所有注冊(cè)的 SpringBootExceptionReporter 實(shí)現(xiàn)類的實(shí)例。
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
      new Class[] { ConfigurableApplicationContext.class }, context);
      
      
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
   ClassLoader classLoader = getClassLoader();
   // Use names and ensure unique to protect against duplicates
   //加載指定 type 類型的工廠類名稱。
   Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
   //根據(jù)工廠類名稱創(chuàng)建實(shí)例對(duì)象。
   //這個(gè)方法將通過反射實(shí)例化工廠類,并調(diào)用其對(duì)應(yīng)的靜態(tài)工廠方法或構(gòu)造函數(shù)。
   List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}

2.2.11準(zhǔn)備上下文prepareContext

/*
 * @description:準(zhǔn)備應(yīng)用程序的上下文。它將配置環(huán)境設(shè)置到上下文中,調(diào)用初始化器進(jìn)行上下文初始化,注冊(cè)一些特定的單例 Bean,加載應(yīng)用程序的資源,
 * 并通知監(jiān)聽器應(yīng)用程序的上下文已準(zhǔn)備好和已加載完成。
 * 這個(gè)過程是 Spring Boot 應(yīng)用程序啟動(dòng)過程中的重要一環(huán),確保應(yīng)用程序上下文正確配置和初始化。
 * @author: wangwei
 * @date: 2023/7/26 20:55
 * @param: 
 * @return: 
 **/

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
      SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
   //將應(yīng)用程序的配置環(huán)境 environment 設(shè)置到上下文 context 中。
   context.setEnvironment(environment);
   //對(duì)應(yīng)用程序的上下文進(jìn)行后置處理。這是 Spring 的擴(kuò)展點(diǎn),可以在應(yīng)用程序上下文創(chuàng)建后,對(duì)其進(jìn)行額外的定制。
   postProcessApplicationContext(context);
   //調(diào)用應(yīng)用程序的初始化器(ApplicationContextInitializer)對(duì)上下文進(jìn)行初始化。初始化器是一種在 Spring Boot 應(yīng)用程序啟動(dòng)時(shí)自動(dòng)執(zhí)行的擴(kuò)展機(jī)制。
   applyInitializers(context);
   //通知監(jiān)聽器,應(yīng)用程序的上下文已準(zhǔn)備好。這是應(yīng)用程序啟動(dòng)過程中通知監(jiān)聽器的一個(gè)生命周期事件。
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      //如果應(yīng)用程序的上下文沒有父級(jí)上下文,則記錄啟動(dòng)信息。
      logStartupInfo(context.getParent() == null);
      //記錄應(yīng)用程序的啟動(dòng)配置信息和配置文件。
      logStartupProfileInfo(context);
   }
   // Add boot specific singleton beans
   //向上下文注冊(cè)特定的單例 Bean:
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   //注冊(cè) applicationArguments,應(yīng)用程序的命令行參數(shù)對(duì)象。
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      //注冊(cè) printedBanner,打印的橫幅對(duì)象。
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   if (beanFactory instanceof DefaultListableBeanFactory) {
      //設(shè)置是否允許覆蓋 Bean 定義,即是否允許多次注冊(cè)同一名稱的 Bean。
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   // Load the sources
   //設(shè)置是否允許覆蓋 Bean 定義,即是否允許多次注冊(cè)同一名稱的 Bean。
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   //設(shè)置是否允許覆蓋 Bean 定義,即是否允許多次注冊(cè)同一名稱的 Bean。
   load(context, sources.toArray(new Object[0]));
   //設(shè)置是否允許覆蓋 Bean 定義,即是否允許多次注冊(cè)同一名稱的 Bean。
   listeners.contextLoaded(context);
}

2.2.12.刷新上下文refreshContext

//Spring上下文刷新:這將觸發(fā) Spring Bean 的實(shí)例化和依賴注入等過程。
			refreshContext(context);

private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}
	
//Spring 應(yīng)用程序上下文刷新的核心方法,負(fù)責(zé)進(jìn)行初始化、配置和處理各種 Bean,使得應(yīng)用程序上下文準(zhǔn)備好運(yùn)行,可以在后續(xù)的運(yùn)行中使用和管理 Bean。
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      //準(zhǔn)備應(yīng)用程序上下文進(jìn)行刷新操作。
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      //準(zhǔn)備應(yīng)用程序上下文進(jìn)行刷新操作。
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      //準(zhǔn)備 Bean 工廠供在上下文中使用
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
          //允許上下文子類后處理 Bean 工廠,做一些額外的配置。
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         //調(diào)用注冊(cè)在上下文中的 Bean 工廠后置處理器,這些后置處理器用于在 Bean 工廠實(shí)例化 Bean 之前修改、擴(kuò)展或配置 Bean 工廠。
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
        //注冊(cè) Bean 后置處理器(BeanPostProcessor)到 Bean 工廠中。Bean 后置處理器在 Bean 的實(shí)例化、初始化以及銷毀的過程中,可以對(duì) Bean 進(jìn)行攔截和處理。
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         //初始化消息資源,用于國(guó)際化和本地化消息處理。
         initMessageSource();

         // Initialize event multicaster for this context.
         //初始化應(yīng)用程序事件廣播器,用于發(fā)布和處理應(yīng)用程序事件。
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         //在特定上下文子類中,進(jìn)行其他特殊 Bean 的初始化和配置。
         onRefresh();

         // Check for listener beans and register them.
         //檢查并注冊(cè)事件監(jiān)聽器
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         //實(shí)例化所有剩余的(非懶加載)單例 Bean。
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         //完成刷新操作,發(fā)布 Spring 容器刷新事件。
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         //清空 Spring 核心中的常見的元數(shù)據(jù)緩存,以便在運(yùn)行時(shí)不再需要單例 Bean 的元數(shù)據(jù)信息。
         resetCommonCaches();
      }
   }
}

2.2.13上下文后置處理

//在默認(rèn)情況下不執(zhí)行任何操作,留給子類去擴(kuò)展和實(shí)現(xiàn)。
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}

2.2.14停止計(jì)時(shí)器

//停止 
StopWatch 的計(jì)時(shí),并記錄上一次計(jì)時(shí)任務(wù)的執(zhí)行時(shí)間。
public void stop() throws IllegalStateException {
    if (this.currentTaskName == null) {
        throw new IllegalStateException("Can't stop StopWatch: it's not running");
    } else {
        long lastTime = System.currentTimeMillis() - this.startTimeMillis;
        this.totalTimeMillis += lastTime;
        this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
        if (this.keepTaskList) {
            this.taskList.add(this.lastTaskInfo);
        }

        ++this.taskCount;
        this.currentTaskName = null;
    }
}

2.2.15.打印應(yīng)用程序啟動(dòng)信息

//如果設(shè)置了 logStartupInfo,則會(huì)記錄應(yīng)用程序啟動(dòng)信息,包括啟動(dòng)時(shí)間和主類等。
if (this.logStartupInfo) {
   new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}

2.2.16 通知監(jiān)聽器,應(yīng)用程序正在運(yùn)行

//是通知所有注冊(cè)的 SpringApplicationRunListener 實(shí)現(xiàn)類,應(yīng)用程序上下文已經(jīng)啟動(dòng)完成。
public void started(ConfigurableApplicationContext context) {
   for (SpringApplicationRunListener listener : this.listeners) {
      listener.started(context);
   }
}

2.2.17執(zhí)行所有Runner

//它在應(yīng)用程序上下文創(chuàng)建完成后,調(diào)用所有注冊(cè)的 ApplicationRunner 和 CommandLineRunner bean 的 run() 方法。
private void callRunners(ApplicationContext context, ApplicationArguments args) {
   List<Object> runners = new ArrayList<>();
   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);
      }
   }
}

2.2.18.返回上下文對(duì)象context

return context;

到了這里,關(guān)于Spring Boot——Spring Boot啟動(dòng)原理的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 微信小程序的授權(quán)登錄-Java 后端 (Spring boot)

    微信開發(fā)文檔鏈接:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html 一個(gè)可以測(cè)試的微信小程序 此微信小程序的APPID和APPscret(至開發(fā)者后臺(tái)獲?。?從時(shí)序圖我們可以了解到流程大致分為兩步: 小程序端獲取code后傳給Java后臺(tái) Java后臺(tái)獲取code后向微信后臺(tái)接口

    2024年02月09日
    瀏覽(97)
  • “從零開始學(xué)習(xí)Spring Boot:快速搭建Java后端開發(fā)環(huán)境“

    標(biāo)題:從零開始學(xué)習(xí)Spring Boot:快速搭建Java后端開發(fā)環(huán)境 摘要:本文將介紹如何從零開始學(xué)習(xí)Spring Boot,并詳細(xì)講解如何快速搭建Java后端開發(fā)環(huán)境。通過本文的指導(dǎo),您將能夠快速搭建一個(gè)基于Spring Boot的Java后端開發(fā)環(huán)境并開始編寫代碼。 正文: 一、準(zhǔn)備工作 在開始之前,

    2024年02月15日
    瀏覽(35)
  • spring boot 啟動(dòng)報(bào)錯(cuò)---java: 無法訪問org.springframework.boot.SpringApplication 錯(cuò)誤的類文件:

    spring boot 啟動(dòng)報(bào)錯(cuò)---java: 無法訪問org.springframework.boot.SpringApplication 錯(cuò)誤的類文件:

    目錄 錯(cuò)誤提示信息: 原因: 解決辦法: 具體步驟: 主要是因?yàn)?spring boot 3.0發(fā)布了 ,在創(chuàng)建項(xiàng)目時(shí),默認(rèn)為3.0 ` 但同時(shí), spring boot 3.0 只支持jdk 17 ,在平時(shí)創(chuàng)建時(shí),都喜歡使用jdk8 由于 spring boot 版本默認(rèn)3.0,同時(shí)jdk選擇的是 8 ,就會(huì)導(dǎo)致這個(gè)錯(cuò)誤 將 spring boot 版本和jdk統(tǒng)一

    2024年02月15日
    瀏覽(16)
  • Spring Boot源碼解讀與原理剖析:深入探索Java開發(fā)的奧秘!

    Spring Boot源碼解讀與原理剖析:深入探索Java開發(fā)的奧秘!

    關(guān)注+點(diǎn)贊+評(píng)論,評(píng)論區(qū)回復(fù)“Spring Boot源碼解讀與原理剖析:深入探索Java開發(fā)的奧秘!” 每篇最多 評(píng)論3條 ?。〔捎贸楠?jiǎng)助手自動(dòng)拉取評(píng)論區(qū)有效評(píng)論送書兩本, 開獎(jiǎng)時(shí)間:9月11號(hào) 承載著作者的厚望,掘金爆火小冊(cè)同名讀物 《Spring Boot源碼解讀與原理剖析》 正式出書!

    2024年02月10日
    瀏覽(19)
  • Spring Boot 整合 Shiro(后端)

    Spring Boot 整合 Shiro(后端)

    1 Shiro 什么是 Shiro 官網(wǎng): http://shiro.apache.org/ 是一款主流的 Java 安全框架,不依賴任何容器,可以運(yùn)行在 Java SE 和 Java EE 項(xiàng)目中,它的主要作用是對(duì)訪問系統(tǒng)的用戶進(jìn)行身份認(rèn)證、 授權(quán)、會(huì)話管理、加密等操作。 Shiro 就是用來解決安全管理的系統(tǒng)化框架。 2 Shiro 核心組件 用

    2024年02月09日
    瀏覽(26)
  • Spring Boot——Spring Boot自動(dòng)配置原理

    Spring Boot——Spring Boot自動(dòng)配置原理

    Spring Boot啟動(dòng)原理 一直在使用Spring Boot特別好奇的是為什么Spring Boot比Spring在項(xiàng)目構(gòu)建和開發(fā)過程中要方便很多,無需編寫大量的配置,Spring Boot自動(dòng)給你配置好了。往往是集成項(xiàng)目依賴之后一鍵使用。于是小編我就學(xué)習(xí)和研究了一下Spring Boot的自動(dòng)配置。 主程序入口示例 :

    2024年02月15日
    瀏覽(14)
  • Android前端+Spring Boot后端 登錄功能實(shí)現(xiàn)

    Android前端+Spring Boot后端 登錄功能實(shí)現(xiàn)

    創(chuàng)建項(xiàng)目后,自己添加包,框架如下 ? userController里的一些內(nèi)容,只供參考,代碼不全,無法實(shí)現(xiàn) 數(shù)據(jù)庫是直接在社區(qū)版IDEA里連接Mysql,在控制臺(tái)端創(chuàng)建的數(shù)據(jù)庫和user表,用于數(shù)據(jù)交互。 Activity包里是Activity Java類,主要響應(yīng)layout包里activity_login.xml等頁面布局內(nèi)的按鈕響應(yīng) a

    2024年02月12日
    瀏覽(23)
  • 【Spring Boot】Spring Boot自動(dòng)加載機(jī)制:簡(jiǎn)化應(yīng)用程序的啟動(dòng)

    在微服務(wù)盛行的今天,快速搭建和啟動(dòng)應(yīng)用程序變得至關(guān)重要。Spring Boot作為Java生態(tài)系統(tǒng)中主流的框架,其自動(dòng)加載機(jī)制使得開發(fā)者能夠快速構(gòu)建和啟動(dòng)應(yīng)用程序。本文將詳細(xì)介紹Spring Boot的自動(dòng)加載機(jī)制,并通過代碼示例加以說明。 首先,我們要了解Spring Boot自動(dòng)加載機(jī)制

    2024年02月11日
    瀏覽(17)
  • 【Spring Boot】Spring Boot源碼解讀與原理剖析

    【Spring Boot】Spring Boot源碼解讀與原理剖析

    承載著作者的厚望,掘金爆火小冊(cè)同名讀物《Spring Boot源碼解讀與原理剖析》正式出書! 本書前身是掘金社區(qū)銷量TOP的小冊(cè)——《Spring Boot源碼解讀與原理剖析》,整個(gè)社區(qū)中有3600+開發(fā)者都不約而同地選擇了這本小冊(cè),也使它成為掘金社區(qū)首屈一指的王牌Spring教程,非常能打

    2024年02月08日
    瀏覽(41)
  • Spring Boot 啟動(dòng)流程

    加載配置 Spring Boot在啟動(dòng)時(shí)會(huì)加載應(yīng)用程序的配置文件(例如application.properties或application.yml),并將其轉(zhuǎn)化為內(nèi)部的配置對(duì)象。 創(chuàng)建應(yīng)用程序上下文 Spring Boot會(huì)創(chuàng)建一個(gè)應(yīng)用程序上下文(ApplicationContext),它是Spring框架的核心容器。應(yīng)用程序上下文負(fù)責(zé)管理Bean的生命周期和

    2024年02月06日
    瀏覽(33)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包