給她講最愛的SpringBoot源碼

来源:https://www.cnblogs.com/jiagooushi/archive/2022/11/03/16854698.html
-Advertisement-
Play Games

1 Spring boot源碼環境構建 推薦環境: idea:2020.3 gradle:版本gradle-6.5.1 jdk:1.8 註意!idea和gradle的版本有相容性問題,要註意搭配 1.1 Spring boot源碼下載 1、從github獲取源碼,網址: https://github ...


1 Spring boot源碼環境構建

推薦環境:

idea:2020.3

gradle:版本gradle-6.5.1

jdk:1.8

註意!idea和gradle的版本有相容性問題,要註意搭配

1.1 Spring boot源碼下載

1、從github獲取源碼,網址:

https://github.com/spring-projects/spring-boot

我們要搭建的是2.4.3.RELEASE版本,所以點擊release 之後在tags查找相應版本或者訪問

https://github.com/spring-projects/spring-boot/releases/tag/v2.4.3

找到 後點擊sourcecode下載源碼壓縮包

file
目錄結構

file
Spring-boot-project 核心代碼,代碼量很多(197508 行)
Spring-boot-tests 測試代碼

2、直接用提供的源碼包(推薦)

將源碼導入到idea。漫長的等待……

file

1.2 Spring boot源碼編譯

1、環境配置

推薦配置:

file
2、開始gradle構建

使用idea的build,不要用gradle的任務

file

看到下麵的BUILE SUCESSFUL表示成功

file

1.3 Spring boot冒煙測試

在springboot-boot-tests模塊下很多冒煙測試的,會拖慢上面的編譯,只留下了一個:

spring-boot-smoke-test-hibernate52工程來進行冒煙測試,打開Hibernate52Application.java文件,直接執行main方法啟動springboot,成功!

org.springframework.boot.tests.hibernate52.Hibernate52Application

package org.springframework.boot.tests.hibernate52;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Hibernate52Application {

	public static void main(String[] args) {
		SpringApplication.run(Hibernate52Application.class, args);
	}

}

執行run

console中出現我們熟悉的圖標。

file

2 Spring boot源碼深度剖析

引言
使用過SpringBoot開發項目的讀者應該都能夠感覺到
SpringBoot的開發完成後,只需要通過執行一個main方法就可以將整個web項目啟動
無需將項目的jar文件放在tomcat下,然後啟動tomcat,進而啟動項目。
除此之外,好多依賴的jar包也無需我們再進行手動配置,減少了配置,
同時也減少了許多xml文件的配置,大大簡化了我們的開發過程
那麼
springboot在啟動的時候到底做了哪些事情?

2.1 Spring boot啟動流程剖析

第一步:new SpringApplication(primarySources)

第二步:run!

2.1.1 Spring boot啟動流程剖析

Debug一下,追蹤一下整個啟動過程

main方法作為程式的入口,執行SpringApplication.run(),傳入參數是啟動類的class對象

file

1)Spring boot源碼入口

@SpringBootApplication
public class Hibernate52Application {

	public static void main(String[] args)  {
		SpringApplication.run(Hibernate52Application.class, args);
	}

}

跟蹤run方法;進入到

參數一可支持多個主要資源。

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

繼續進入到run方法

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

2)構造器(new)

//方法目標
//1、初始化資源載入器(classloader)
//2、處理primarySources
//3、web應用類型推斷 (web、reactive、servlet)
//4、通過spring.factories載入配置類並初始化監聽器 (SPI) 【重點】
//5、提取主類
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		//null;資源載入器,用來獲取 Resource 和 classLoader 以及載入資源
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		//存放主載入類;set中可同時創建多個Application,最後要解析這個來源上的註解
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		//推斷 web 類型:servlet 或 reactive
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
//		0個,從spring.factories中找出Bootstrapper對應的屬性
		this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
//		7個,設置初始化器,從spring.factories中找出ApplicationContextInitializer對應的屬性
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//		9個,設置監聽器 從spring.factories中找出ApplicationListener對應的屬性
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		//找出主函數main的類
		this.mainApplicationClass = deduceMainApplicationClass();
	}

上面 的代碼最終會調用到getSpringFactoriesInstances,從spring.factories載入屬性配置

載入核心源碼如下

下麵代碼

首先會用classLoader載入類路徑下的所有spring.factories的配置內容,loadSpringFactories方法將返回一個key=介面名,value=實現類集合的Map結構

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    // 先試著取緩存
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
             // 獲取所有spring.factories的URL(3個地方)
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
            // 遍歷URL
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
                 // 載入每個URL中的properties配置
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
           
				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());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
            //加入緩存
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

file

主要的spring.factories

spring-boot-2.4.3/spring-boot-project/spring-boot-autoconfigure/build/resources/main/META-INF/spring.factories


spring-boot-2.4.3/spring-boot-project/spring-boot/build/resources/main/META-INF/spring.factories


spring-beans-5.3.4.jar!/META-INF/spring.factories

構造器流程總結

1、處理資源載入器、主要資源primarySources

2、web應用類型推斷

3、從spring.factories中找出引導包裝器、初始化器、監聽器

4、設置應用程式主類

3)boot運行(run)

發佈事件

列印banner

初始化ioc容器,啟動tomcat

七大步驟

//七大步驟
	public ConfigurableApplicationContext run(String... args) {
		//計時器
		StopWatch stopWatch = new StopWatch();
		stopWatch.start(); //開始計時
		//  創建啟動上下文對象
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		// 可配置的程式容器
		ConfigurableApplicationContext context = null;
		// 設置屬性 不重要
		configureHeadlessProperty();
			// 第一步:獲取並啟動監聽器    從spring.factories文件中載入【測試點】
		SpringApplicationRunListeners listeners = getRunListeners(args);
			//監聽器發佈ApplicationStartingEvent 事件.
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
			// 對參數進行包裝(ApplicationArguments)
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			//第二步:準備應用程式環境【關鍵點】
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			// 配置忽略bean的信息(不重要)
			configureIgnoreBeanInfo(environment);
			//第三步: 列印banner(可自定義,參考講義)【關鍵點】
			Banner printedBanner = printBanner(environment);
            // 第四步:創建spring容器
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);
			//第五步:準備 applicationContext
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
			//第六步:ioc的refresh創建容器,初始化bean,tomcat也在這裡被啟動起來 【關鍵點】
			refreshContext(context); 
			//第七步:上下文刷新後觸發(空方法)
			afterRefresh(context, applicationArguments);
			stopWatch.stop();//停止計時
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			// 發佈started事件
			listeners.started(context);
			//執行runner的run方法 【測試點】
			callRunners(context, applicationArguments);
		} catch (Throwable ex) {
			// 異常處理
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			// 觸發running事件
			listeners.running(context);
		} catch (Throwable ex) {
			handleRunFailure(context, ex, null);
			throw new IllegalStateException(ex);
		}
		// 返回最終構建的容器對象
		return context;
	}

2.1.2 Spring boot七大步驟詳解

1)獲取並啟動監聽器

這裡的啟動監聽就是我們需要監聽SpringBoot的啟動流程監聽,實現SpringApplicationRunListener類即可監聽

	//獲取spring.factories中 key為SpringApplicationRunListener的對象實例。
	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[]{SpringApplication.class, String[].class};
		// 通過從 spring.factories 中獲取 SpringApplicationRunListener 類型的配置類
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
				this.applicationStartup);
	}

查看具體SpringApplicationRunListener都有哪些方法

package org.springframework.boot;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.support.SpringFactoriesLoader;


public interface SpringApplicationRunListener {

	/**
	 * Called immediately when the run method has first started. Can be used for very
	 * early initialization.
	 * @param bootstrapContext the bootstrap context
	 */
	//當調用run方法後會立即調用,可以用於非常早期的初始化
	default void starting(ConfigurableBootstrapContext bootstrapContext) {
		starting();
	}

	/**
	 * Called immediately when the run method has first started. Can be used for very
	 * early initialization.
	 * @deprecated since 2.4.0 in favor of {@link #starting(ConfigurableBootstrapContext)}
	 */
	@Deprecated
	default void starting() {
	}

	/**
	 * Called once the environment has been prepared, but before the
	 * {@link ApplicationContext} has been created.
	 * @param bootstrapContext the bootstrap context
	 * @param environment the environment
	 */
	//環境準備好之後調用
	default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
			ConfigurableEnvironment environment) {
		environmentPrepared(environment);
	}

	/**
	 * Called once the environment has been prepared, but before the
	 * {@link ApplicationContext} has been created.
	 * @param environment the environment
	 * @deprecated since 2.4.0 in favor of
	 * {@link #environmentPrepared(ConfigurableBootstrapContext, ConfigurableEnvironment)}
	 */
	@Deprecated
	default void environmentPrepared(ConfigurableEnvironment environment) {
	}

	/**
	 * Called once the {@link ApplicationContext} has been created and prepared, but
	 * before sources have been loaded.
	 * @param context the application context
	 */
	//在載入資源之前,ApplicationContex準備好之後調用
	default void contextPrepared(ConfigurableApplicationContext context) {
	}

	/**
	 * Called once the application context has been loaded but before it has been
	 * refreshed.
	 * @param context the application context
	 */
	//在載入應用程式上下文但在其刷新之前調用
	default void contextLoaded(ConfigurableApplicationContext context) {
	}

	/**
	 * The context has been refreshed and the application has started but
	 * {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
	 * ApplicationRunners} have not been called.
	 * @param context the application context.
	 * @since 2.0.0
	 */
	/**
	 * 上下文已經刷新且應用程式已啟動且所有{@link CommandLineRunner commandLineRunner}
	 * 和{@link ApplicationRunner ApplicationRunners}未調用之前調用
	 */
	default void started(ConfigurableApplicationContext context) {
	}

	/**
	 * Called immediately before the run method finishes, when the application context has
	 * been refreshed and all {@link CommandLineRunner CommandLineRunners} and
	 * {@link ApplicationRunner ApplicationRunners} have been called.
	 * @param context the application context.
	 * @since 2.0.0
	 */
	/**
	 * 當應用程式上下文被刷新並且所有{@link CommandLineRunner commandLineRunner}
	 * 和{@link ApplicationRunner ApplicationRunners}都已被調用時,在run方法結束之前立即調用。
	 */

	default void running(ConfigurableApplicationContext context) {
	}

	/**
	 * Called when a failure occurs when running the application.
	 * @param context the application context or {@code null} if a failure occurred before
	 * the context was created
	 * @param exception the failure
	 * @since 2.0.0
	 */
	//在啟動過程發生失敗時調用
	default void failed(ConfigurableApplicationContext context, Throwable exception) {
	}

}

2)準備應用程式環境

創建並配置SpringBooty應用將要使用的Environment

//不看細節,看返回的環境數據即可
	//創建並配置SpringBooty應用將要使用的Environment
	//過程如下:
	//	1、創建配置環境 ConfigurableEnvironment
	//	2、載入屬性文件資源
	//	3、配置監聽
	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
													   DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
		// 根據不同的web類型創建不同實現的Environment對象
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 配置環境
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
		// 發送環境已準備完成事件
		listeners.environmentPrepared(bootstrapContext, environment);
		DefaultPropertiesPropertySource.moveToEnd(environment);
		// 根據命令行參數中spring.profiles.active屬性配置Environment對象中的activeProfile(比如dev、prod、test)
		configureAdditionalProfiles(environment);
		// 綁定環境中spring.main屬性綁定到SpringApplication對象中
		bindToSpringApplication(environment);
		// 如果用戶使用spring.main.web-application-type屬性手動設置了webApplicationType
		if (!this.isCustomEnvironment) {
			// 將環境對象轉換成用戶設置的webApplicationType相關類型,他們是繼承同一個父類,直接強轉
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment; //環境查看(控制台)
	}

 

查看環境

file

3)控制台列印Banner

	private Banner printBanner(ConfigurableEnvironment environment) {
		// banner模式,可以是console、log、off
		if (this.bannerMode == Banner.Mode.OFF) {
			return null;
		}
		ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
				: new DefaultResourceLoader(null);
		SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
		//日誌列印banner
		if (this.bannerMode == Mode.LOG) {
			return bannerPrinter.print(environment, this.mainApplicationClass, logger);
		}
		//控制台列印banner
		return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
	}

最終列印

通過org.springframework.boot.ResourceBanner#printBanner

	@Override
	public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
		try {
			String banner = StreamUtils.copyToString(this.resource.getInputStream(),
					environment.getProperty("spring.banner.charset", Charset.class, StandardCharsets.UTF_8));

			for (PropertyResolver resolver : getPropertyResolvers(environment, sourceClass)) {
				banner = resolver.resolvePlaceholders(banner);
			}
			out.println(banner);//此處列印
		}
		catch (Exception ex) {
			logger.warn(LogMessage.format("Banner not printable: %s (%s: '%s')", this.resource, ex.getClass(),
					ex.getMessage()), ex);
		}
	}

截圖如下

file

4)創建應用上下文對象

	protected ConfigurableApplicationContext createApplicationContext() {
		return this.applicationContextFactory.create(this.webApplicationType);
	}

調用到下麵

public interface ApplicationContextFactory {

	/**
	 * A default {@link ApplicationContextFactory} implementation that will create an
	 * appropriate context for the {@link 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);
		}
	};

5)準備應用上下文

核心代碼如下

	/**
	 * Spring容器準備
	 */
	private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
								ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
								ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);//設置環境
		postProcessApplicationContext(context);//設置上下文
		// 執行所有ApplicationContextInitializer對象的initialize方法(這些對象是通過讀取spring.factories載入)
		applyInitializers(context);//設置初始化工作(不用看)
		// 發佈上下文準備完成事件到所有監聽器
		listeners.contextPrepared(context);//觸發監聽器
		bootstrapContext.close(context);
		if (this.logStartupInfo) { //日誌操作
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// 獲取工廠 DefaultListableBeanFactory
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		//註冊單例對象
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			//註冊banner單例對象
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			//是否覆蓋bean
			((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");
		//載入(業務類的註解需要掃描)  bean到上下文
		load(context, sources.toArray(new Object[0]));
		// 發送上下文載入完成事件
		listeners.contextLoaded(context);
	}

6)刷新應用程式上下文

ioc容器初始化

重要!

tomcat的啟動在這裡!

  //核心方法
	private void refreshContext(ConfigurableApplicationContext context) {
		// ……
		// 開始執行啟動容器(調用模板方法)
		refresh((ApplicationContext) context);
	}

擴展問題:

如果在springboot里使用了web容器,它是如何啟動的?

refreshContext 裡面,沿著 refresh - onRefresh,註意是 ServletWebServerApplicationContext的

我們說,在普通的spring里onRefresh是個空方法,留給子類去實現,那麼,

看看這個 ServletWebServerApplicationContext 實現類它的 onRefresh偷偷幹了些啥見不得人的事?……

7)容器回調方法

空方法

	protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
	}

run方法啟動後
主要做如下幾件事情:

1、發出啟動結束事件

2、執行實現ApplicationRunner、CommandLineRunner的run方法
3、發佈應用程式已啟動(ApplicationStartedEvent)事件

4、異常處理

小疑問:

boot啟動了一個web,那麼一定有一個DispacherServlet,它是啥時候被載入的呢???

提示:

@EnableAutoConfiguration 註解的spi,在spring-boot-autoconfigure的spring.factories里

EnableAutoConfiguration的載入類里有個:DispatcherServletAutoConfiguration 做了自動裝配

秘密就藏在這貨里

那自動裝配又是什麼鬼呢?除了DS,還有各種starter,怎麼載入的呢?下節課繼續……

2.2 boot自定義Banner

banner自動生成工具,ascii文字展示

http://www.network-science.de/ascii/

file
Spring boot啟動如下

file
在路徑

\spring-boot-tests\spring-boot-smoke-tests\spring-boot-smoke-test-hibernate52\src\main\resources

下創建banner.txt(註意:文件名稱不能變,否則無法載入)

banner.txt內容如下

88b           d88             88888888ba                                  
888b         d888             88      "8b                          ,d     
88`8b       d8'88             88      ,8P                          88     
88 `8b     d8' 88 8b       d8 88aaaaaa8P'  ,adPPYba,   ,adPPYba, MM88MMM  
88  `8b   d8'  88 `8b     d8' 88""""""8b, a8"     "8a a8"     "8a  88     
88   `8b d8'   88  `8b   d8'  88      `8b 8b       d8 8b       d8  88     
88    `888'    88   `8b,d8'   88      a8P "8a,   ,a8" "8a,   ,a8"  88,    
88     `8'     88     Y88'    88888888P"   `"YbbdP"'   `"YbbdP"'   "Y888  
                      d8'                                                 
                     d8'                                               

2.3 面試題

1、Spring Boot 的核心註解是哪個?它主要由哪幾個註解組成的

啟動類上面的註解是@SpringBootApplication,它也是 Spring Boot 的核心註解,主要組合包含了以下 3 個註解:

  • @SpringBootConfiguration:組合了 @Configuration 註解,實現配置文件的功能。
  • @EnableAutoConfiguration:打開自動配置的功能,也可以關閉某個自動配置的選項,如關閉數據源自動配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。

​ 組合了

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
  • @ComponentScan:Spring組件掃描

2、Spring Boot 自動配置原理是什麼?

註解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自動配置的核心,

@EnableAutoConfiguration 給容器導入META-INF/spring.factories 里定義的自動配置類。

篩選有效的自動配置類。

每一個自動配置類結合對應的 xxx.java 讀取配置文件進行自動配置功能

3、Spring Boot 中的 starter 到底是什麼 ?

首先,這個 Starter 並非什麼新的技術點,基本上還是基於 Spring 已有功能來實現的。首先它提供了一個自動化配置類,一般命名為 XXXAutoConfiguration ,在這個配置類中通過條件註解來決定一個配置是否生效(條件註解就是 Spring 中原本就有的),然後它還會提供一系列的預設配置,也允許開發者根據實際情況自定義相關配置,然後通過類型安全的屬性註入將這些配置屬性註入進來,新註入的屬性會代替掉預設屬性。正因為如此,很多第三方框架,我們只需要引入依賴就可以直接使用了。當然,開發者也可以自定義 Starter

4、運行 Spring Boot 有哪幾種方式?

1)打包用命令或者放到容器中運行

2)用 Maven/ Gradle 插件運行

3)直接執行 main 方法運行

本文由傳智教育博學谷教研團隊發佈。

如果本文對您有幫助,歡迎關註點贊;如果您有任何建議也可留言評論私信,您的支持是我堅持創作的動力。

轉載請註明出處!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 您好,我是湘王,這是我的博客園,歡迎您來,歡迎您再來~ 從之前的Lambda表達式的演變過程可以知道,Lambda表達式其實是一個對匿名內部類的簡化過程:去掉了多餘的語法修飾,只保留最最核心的部分。在Java中類似這種使用匿名內部類寫代碼的場景非常多,比如Runnable介面,就是典型的最好使用La ...
  • 本節內容會用到之前給大家講過的這兩篇: 2流高手速成記(之六):從SpringBoot到SpringCloudAlibaba 2流高手速成記(之三):SpringBoot整合mybatis/mybatis-plus實現數據持久化 鏈接掛出來,方便咱們中途對比著看 老規矩,先放出本節的項目結構: 我們 ...
  • XML 官方文檔:https://www.w3school.com.cn/xml/index.asp 1.為什麼需要xml? 需求1:兩個程式間進行數據通信? 需求2:給一臺伺服器,做一個配置文件,當伺服器程式啟動時,去讀取它應當監聽的埠號、還有連接資料庫的用戶名和密碼 spring中的IOC配置 ...
  • 大家好,我是metahuber,數字宇宙探索者。 本系列教程是Python的入門教程,本篇文章是此教程的第一篇,希望大家多多關註。 在學習Python之前,我們首先瞭解下什麼是編程語言。 說到編程語言,還需要從程式說起。 其實,程式就是一系列指令,電腦之所以能夠工作,根本的原因是它能夠識別人類發出 ...
  • 登錄介面分析 登錄分為多方式登錄和驗證碼登錄方式 多方式登錄 1)前臺提供賬號密碼,賬號可能是 用戶名、手機號、郵箱等 介面: 後臺只需要提供一個多方式登錄介面即可 - 多方式登錄介面 多方式登錄介面 前端輸入完賬號和密碼,點擊登錄,向後端發送請求進行校驗用戶登錄數據 urls.py from dj ...
  • 簡介: 適配器模式屬於結構型設計模式。 將一個類的介面轉換成可應用的相容介面。適配器使原本由於介面不相容而不能一起工作的那些類可以一起工作。 適配器模式有兩種實現方案,一種是繼承的方式,一種是組合的方式。 適用場景: 相容不方便更改的“祖傳”代碼。 歸納具有相似點的模塊,比如Laravel File ...
  • 前言 所謂熱部署,簡單來說,就是代碼修改後不需重啟項目就可自動載入出新的內容。 ==註意:熱部署在 debug 調試模式下才生效!== IDEA 配置 在 IDE(IDEA)中開啟相關項目自動構建選項 開啟編譯器設置中修改後自動編譯的選項(下圖是 IDEA 2021版本,其他版本可能在其他位置) S ...
  • 作者:張富春(ahfuzhang),轉載時請註明作者和引用鏈接,謝謝! cnblogs博客 zhihu Github 公眾號:一本正經的瞎扯 團隊中之前的文件下載做得比較複雜,因為擔心量太大,是後臺做非同步的下載,最終生成文件,傳送文件到CDN伺服器,最後再告訴用戶下載鏈接。 其實在查詢介面中就可以實 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...