色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Spring Boot的注解原理是什么

5jek_harmonyos ? 來源:博客園 ? 作者:kosamino ? 2021-08-27 09:24 ? 次閱讀

首先,先看SpringBoot的主配置類:

@SpringBootApplicationpublic class StartEurekaApplication

{

public static void main(String[] args)

{

SpringApplication.run(StartEurekaApplication.class, args);

}

}

點進@SpringBootApplication來看,發現@SpringBootApplication是一個組合注解。

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = {

@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),

@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

public @interface SpringBootApplication {

}

首先我們先來看 @SpringBootConfiguration:

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented@Configurationpublic @interface SpringBootConfiguration {

}

可以看到這個注解除了元注解以外,就只有一個@Configuration,那也就是說這個注解相當于@Configuration,所以這兩個注解作用是一樣的,它讓我們能夠去注冊一些額外的Bean,并且導入一些額外的配置。

那@Configuration還有一個作用就是把該類變成一個配置類,不需要額外的XML進行配置。所以@SpringBootConfiguration就相當于@Configuration。進入@Configuration,發現@Configuration核心是@Component,說明Spring的配置類也是Spring的一個組件。

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented@Componentpublic @interface Configuration {

@AliasFor

annotation = Component.class

String value() default “”;

}

繼續來看下一個@EnableAutoConfiguration,這個注解是開啟自動配置的功能。

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented@Inherited@AutoConfigurationPackage@Import({AutoConfigurationImportSelector.class})

public @interface EnableAutoConfiguration {

String ENABLED_OVERRIDE_PROPERTY = “spring.boot.enableautoconfiguration”;

Class《?》[] exclude() default {};

String[] excludeName() default {};

}

可以看到它是由 @AutoConfigurationPackage,@Import(EnableAutoConfigurationImportSelector.class)這兩個而組成的,我們先說@AutoConfigurationPackage,他是說:讓包中的類以及子包中的類能夠被自動掃描到spring容器中。

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented@Inherited@Import({Registrar.class})

public @interface AutoConfigurationPackage {

}

使用@Import來給Spring容器中導入一個組件 ,這里導入的是Registrar.class。來看下這個Registrar:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

Registrar() {

}

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());

}

public Set《Object》 determineImports(AnnotationMetadata metadata) {

return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));

}

}

就是通過以上這個方法獲取掃描的包路徑,可以debug查看具體的值:

那metadata是什么呢,可以看到是標注在@SpringBootApplication注解上的DemosbApplication,也就是我們的主配置類Application:

其實就是將主配置類(即@SpringBootApplication標注的類)的所在包及子包里面所有組件掃描加載到Spring容器。因此我們要把DemoApplication放在項目的最高級中(最外層目錄)。

看看注解@Import(AutoConfigurationImportSelector.class),@Import注解就是給Spring容器中導入一些組件,這里傳入了一個組件的選擇器:AutoConfigurationImportSelector。

可以從圖中看出AutoConfigurationImportSelector 繼承了 DeferredImportSelector 繼承了 ImportSelector,ImportSelector有一個方法為:selectImports。將所有需要導入的組件以全類名的方式返回,這些組件就會被添加到容器中。

public String[] selectImports(AnnotationMetadata annotationMetadata) {

if (!this.isEnabled(annotationMetadata)) {

return NO_IMPORTS;

} else {

AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);

AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry =

this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);

return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());

}

}

會給容器中導入非常多的自動配置類(xxxAutoConfiguration);就是給容器中導入這個場景需要的所有組件,并配置好這些組件。

有了自動配置類,免去了我們手動編寫配置注入功能組件等的工作。那是如何獲取到這些配置類的呢,看看下面這個方法:

protected AutoConfigurationImportSelector.AutoConfigurationEntry

getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {

if (!this.isEnabled(annotationMetadata)) {

return EMPTY_ENTRY;

} else {

AnnotationAttributes attributes = this.getAttributes(annotationMetadata);

List《String》 configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

configurations = this.removeDuplicates(configurations);

Set《String》 exclusions = this.getExclusions(annotationMetadata, attributes);

this.checkExcludedClasses(configurations, exclusions);

configurations.removeAll(exclusions);

configurations = this.filter(configurations, autoConfigurationMetadata);

this.fireAutoConfigurationImportEvents(configurations, exclusions);

return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);

}

}

我們可以看到getCandidateConfigurations()這個方法,他的作用就是引入系統已經加載好的一些類,到底是那些類呢:

protected List《String》 getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {

List《String》 configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());

Assert.notEmpty(configurations,

“No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.”);

return configurations;

}

public static List《String》 loadFactoryNames(Class《?》 factoryClass, @Nullable ClassLoader classLoader) {

String factoryClassName = factoryClass.getName();

return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());

}

會從META-INF/spring.factories中獲取資源,然后通過Properties加載資源:

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

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

}

}

}

可以知道SpringBoot在啟動的時候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的值,將這些值作為自動配置類導入到容器中,自動配置類就生效,幫我們進行自動配置工作。以前我們需要自己配置的東西,自動配置類都幫我們完成了。如下圖可以發現Spring常見的一些類已經自動導入。

接下來看@ComponentScan注解,@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }),這個注解就是掃描包,然后放入spring容器。

@ComponentScan(excludeFilters = {

@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}),

@Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class})})

public @interface SpringBootApplication {}

總結下@SpringbootApplication:就是說,他已經把很多東西準備好,具體是否使用取決于我們的程序或者說配置。

接下來繼續看run方法:

public static void main(String[] args) {

SpringApplication.run(Application.class, args);

}

來看下在執行run方法到底有沒有用到哪些自動配置的東西,我們點進run:

public ConfigurableApplicationContext run(String.。. args) {

//計時器

StopWatch stopWatch = new StopWatch();

stopWatch.start();

ConfigurableApplicationContext context = null;

Collection《SpringBootExceptionReporter》 exceptionReporters = new ArrayList();

this.configureHeadlessProperty();

//監聽器

SpringApplicationRunListeners listeners = this.getRunListeners(args);

listeners.starting();

Collection exceptionReporters;

try {

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);

this.configureIgnoreBeanInfo(environment);

Banner printedBanner = this.printBanner(environment);

//準備上下文

context = this.createApplicationContext();

exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);

//預刷新context

this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);

//刷新context

this.refreshContext(context);

//刷新之后的context

this.afterRefresh(context, applicationArguments);

stopWatch.stop();

if (this.logStartupInfo) {

(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);

}

listeners.started(context);

this.callRunners(context, applicationArguments);

} catch (Throwable var10) {

this.handleRunFailure(context, var10, exceptionReporters, listeners);

throw new IllegalStateException(var10);

}

try {

listeners.running(context);

return context;

} catch (Throwable var9) {

this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);

throw new IllegalStateException(var9);

}

}

那我們關注的就是 refreshContext(context); 刷新context,我們點進來看。

private void refreshContext(ConfigurableApplicationContext context) {

refresh(context);

if (this.registerShutdownHook) {

try {

context.registerShutdownHook();

}

catch (AccessControlException ex) {

// Not allowed in some environments.

}

}

}

我們繼續點進refresh(context);

protected void refresh(ApplicationContext applicationContext) {

Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);

((AbstractApplicationContext) applicationContext).refresh();

}

他會調用 ((AbstractApplicationContext) applicationContext).refresh();方法,我們點進來看:

public void refresh() throws BeansException, IllegalStateException {

synchronized (this.startupShutdownMonitor) {

// Prepare this context for refreshing.

prepareRefresh();

// Tell the subclass to refresh the internal bean factory.

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.

prepareBeanFactory(beanFactory);

try {

// Allows post-processing of the bean factory in context subclasses.

postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.

invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.

registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.

initMessageSource();

// Initialize event multicaster for this context.

initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.

onRefresh();

// Check for listener beans and register them.

registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.

finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.

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.。.

resetCommonCaches();

}

}

}

由此可知,就是一個spring的bean的加載過程。繼續來看一個方法叫做 onRefresh():

protected void onRefresh() throws BeansException {

// For subclasses: do nothing by default.

}

他在這里并沒有直接實現,但是我們找他的具體實現:

比如Tomcat跟web有關,我們可以看到有個ServletWebServerApplicationContext:

@Overrideprotected void onRefresh() {

super.onRefresh();

try {

createWebServer();

}

catch (Throwable ex) {

throw new ApplicationContextException(“Unable to start web server”, ex);

}

}

可以看到有一個createWebServer();方法他是創建web容器的,而Tomcat不就是web容器,那是如何創建的呢,我們繼續看:

private void createWebServer() {

WebServer webServer = this.webServer;

ServletContext servletContext = getServletContext();

if (webServer == null && servletContext == null) {

ServletWebServerFactory factory = getWebServerFactory();

this.webServer = factory.getWebServer(getSelfInitializer());

}

else if (servletContext != null) {

try {

getSelfInitializer().onStartup(servletContext);

}

catch (ServletException ex) {

throw new ApplicationContextException(“Cannot initialize servlet context”,

ex);

}

}

initPropertySources();

}

factory.getWebServer(getSelfInitializer());他是通過工廠的方式創建的。

public interface ServletWebServerFactory {

WebServer getWebServer(ServletContextInitializer.。. initializers);

}

可以看到 它是一個接口,為什么會是接口。因為我們不止是Tomcat一種web容器。

我們看到還有Jetty,那我們來看TomcatServletWebServerFactory:

@Overridepublic WebServer getWebServer(ServletContextInitializer.。. initializers) {

Tomcat tomcat = new Tomcat();

File baseDir = (this.baseDirectory != null) ? this.baseDirectory

: createTempDir(“tomcat”);

tomcat.setBaseDir(baseDir.getAbsolutePath());

Connector connector = new Connector(this.protocol);

tomcat.getService().addConnector(connector);

customizeConnector(connector);

tomcat.setConnector(connector);

tomcat.getHost().setAutoDeploy(false);

configureEngine(tomcat.getEngine());

for (Connector additionalConnector : this.additionalTomcatConnectors) {

tomcat.getService().addConnector(additionalConnector);

}

prepareContext(tomcat.getHost(), initializers);

return getTomcatWebServer(tomcat);

}

那這塊代碼,就是我們要尋找的內置Tomcat,在這個過程當中,我們可以看到創建Tomcat的一個流程。

如果不明白的話, 我們在用另一種方式來理解下,大家要應該都知道stater舉點例子。

《dependency》

《groupId》org.springframework.boot《/groupId》

《artifactId》spring-boot-starter-data-redis《/artifactId》《/dependency》《dependency》

《groupId》org.springframework.boot《/groupId》

《artifactId》spring-boot-starter-freemarker《/artifactId》《/dependency》

首先自定義一個stater。

《parent》

《groupId》org.springframework.boot《/groupId》

《artifactId》spring-boot-starter-parent《/artifactId》

《version》2.1.4.RELEASE《/version》

《relativePath/》《/parent》《groupId》com.zgw《/groupId》《artifactId》gw-spring-boot-starter《/artifactId》《version》1.0-SNAPSHOT《/version》《dependencies》

《dependency》

《groupId》org.springframework.boot《/groupId》

《artifactId》spring-boot-autoconfigure《/artifactId》

《/dependency》《/dependencies》

我們先來看maven配置寫入版本號,如果自定義一個stater的話必須依賴spring-boot-autoconfigure這個包,我們先看下項目目錄。

public class GwServiceImpl implements GwService{

@Autowired

GwProperties properties;

@Override

public void Hello()

{

String name=properties.getName();

System.out.println(name+“說:你們好啊”);

}

}

我們做的就是通過配置文件來定制name這個是具體實現。

@Component@ConfigurationProperties(prefix = “spring.gwname”)

public class GwProperties {

String name=“zgw”;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

這個類可以通過@ConfigurationProperties讀取配置文件。

@Configuration@ConditionalOnClass(GwService.class) //掃描類

@EnableConfigurationProperties(GwProperties.class) //讓配置類生效

public class GwAutoConfiguration {

/**

* 功能描述 托管給spring

* @author zgw

* @return

*/

@Bean

@ConditionalOnMissingBean

public GwService gwService()

{

return new GwServiceImpl();

}

}

這個為配置類,為什么這么寫因為,spring-boot的stater都是這么寫的,我們可以參照他仿寫stater,以達到自動配置的目的,然后我們在通過spring.factories也來進行配置。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gw.GwAutoConfiguration

然后這樣一個簡單的stater就完成了,然后可以進行maven的打包,在其他項目引入就可以使用。

鏈接:cnblogs.com/cmt/p/14553189.html

責任編輯:haq

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • spring
    +關注

    關注

    0

    文章

    340

    瀏覽量

    14346
  • Boot
    +關注

    關注

    0

    文章

    149

    瀏覽量

    35842

原文標題:10000 字講清楚 Spring Boot 注解原理

文章出處:【微信號:harmonyos_developer,微信公眾號:harmonyos_developer】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    校園點餐訂餐外賣跑腿Java源碼

    創建一個校園點餐訂餐外賣跑腿系統是一個復雜的項目,涉及到前端、后端、數據庫設計等多個方面。在這里,我可以提供一個簡化的Java后端示例,使用Spring Boot框架來搭建一個基本的API服務。這個
    的頭像 發表于 12-24 14:55 ?119次閱讀
    校園點餐訂餐外賣跑腿Java源碼

    SSM開發環境的搭建教程 SSM與Spring Boot的區別

    SSM開發環境的搭建教程 SSM(Spring+SpringMVC+MyBatis)開發環境的搭建涉及多個步驟,以下是詳細的教程: 創建Maven項目 : 使用Maven工具創建一個新的Maven
    的頭像 發表于 12-16 18:13 ?469次閱讀

    Spring 應用合并之路(二):峰回路轉,柳暗花明

    提醒下,決定拋開 Spring Boot 內置的父子容器方案,完全自己實現父子容器。 如何加載 web 項目? 現在的難題只有一個:如何加載 web 項目?加載完成后,如何持續持有 web 項目?經過思考后,可以創建一個 boot
    的頭像 發表于 12-12 11:22 ?750次閱讀

    Spring事務實現原理

    這些操作。 spring事務有編程式事務和聲明式事務兩種實現方式。編程式事務是通過編寫代碼來管理事務的提交、回滾、以及事務的邊界。這意味著開發者需要在代碼中顯式地調用事務的開始、提交和回滾。聲明式事務是通過配置來管理事務,您可以使用注解或XML配置來
    的頭像 發表于 11-08 10:10 ?828次閱讀
    <b class='flag-5'>Spring</b>事務實現原理

    dubbo3.0 服務導入導出原理

    不管是服務導出還是服務引入,都發生在應用啟動過程中,比如:在啟動類上加上 @EnableDubbo 時,該注解上有一個 @DubboComponentScan 注解
    的頭像 發表于 11-04 15:01 ?151次閱讀
    dubbo3.0 服務導入導出原理

    Spring Cloud Gateway網關框架

    SpringCloud Gateway功能特征如下: (1) 基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 進行構建; (2) 動態路由:能夠匹配任何請求屬性;
    的頭像 發表于 08-22 09:58 ?496次閱讀
    <b class='flag-5'>Spring</b> Cloud Gateway網關框架

    單片機boot0和boot1怎么設置

    單片機Boot0和Boot1簡介 Boot0和Boot1是單片機啟動模式選擇引腳,用于選擇單片機的啟動模式。 Boot0和
    的頭像 發表于 08-22 09:50 ?2607次閱讀

    stm32讀取boot引腳狀態

    在STM32微控制器中,Boot引腳(通常指的是BOOT0和BOOT1引腳)的狀態決定了設備啟動時的引導模式。這些引腳的狀態在復位時被讀取,并據此選擇啟動哪塊存儲器。比如,STM32F103系列
    的頭像 發表于 08-22 09:48 ?1376次閱讀

    stm32boot0和boot1對應哪個p引腳

    STM32系列微控制器是一種廣泛應用于嵌入式系統領域的32位微控制器。在STM32系列微控制器中,BOOT0和BOOT1是兩個重要的引腳,它們用于設置設備的啟動模式。 首先,讓我們了解STM32系列
    的頭像 發表于 08-22 09:40 ?3172次閱讀

    vue+spring boot人員定位系統源碼,實現實時定位、智慧調度、軌跡追蹤

    、機具、物料上定位標簽回傳的位置信息數據,采用多維定位模式,精確定位人、機具、物料的實時位置,實現實時定位、物料標簽配置、智慧調度、軌跡追蹤、工時統計、區域物料統計、電子圍欄等應用功能。 技術架構:java+ spring boot+ v
    的頭像 發表于 08-08 14:27 ?722次閱讀
    vue+<b class='flag-5'>spring</b> <b class='flag-5'>boot</b>人員定位系統源碼,實現實時定位、智慧調度、軌跡追蹤

    玩轉Spring狀態機

    說起Spring狀態機,大家很容易聯想到這個狀態機和設計模式中狀態模式的區別是啥呢?沒錯,Spring狀態機就是狀態模式的一種實現,在介紹Spring狀態機之前,讓我們來看看設計模式中的狀態模式
    的頭像 發表于 06-25 14:21 ?958次閱讀
    玩轉<b class='flag-5'>Spring</b>狀態機

    UWB室內外高精度一體化融合定位系統源碼 UWB技術定位系統應用場景 Java+Spring boot+MYSQL?技術開發

    UWB室內外高精度一體化融合定位系統源碼 UWB技術定位系統應用場景 Java+Spring boot+MYSQL?技術開發 系統聚焦基于UWB(超寬帶)技術的底層定位網絡和定位算法,通過對定位分站
    的頭像 發表于 06-18 10:46 ?489次閱讀
    UWB室內外高精度一體化融合定位系統源碼 UWB技術定位系統應用場景 Java+<b class='flag-5'>Spring</b> <b class='flag-5'>boot</b>+MYSQL?技術開發

    SpingBoot的5個擴展點,超級實用!

    我們在啟動Spring Boot項目的時候,是執行這樣一個方法來啟動的
    的頭像 發表于 02-22 11:28 ?474次閱讀
    SpingBoot的5個擴展點,超級實用!

    Spring事務傳播性的相關知識

    本文主要介紹了Spring事務傳播性的相關知識。
    的頭像 發表于 01-10 09:29 ?452次閱讀
    <b class='flag-5'>Spring</b>事務傳播性的相關知識

    使用Spring Boot 3.2虛擬線程搭建靜態文件服務器

    Spring Boot 3.2 于 2023 年 11 月大張旗鼓地發布,標志著 Java 開發領域的一個關鍵時刻。這一突破性的版本引入了一系列革命性的功能。
    的頭像 發表于 01-09 09:34 ?1129次閱讀
    使用<b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b> 3.2虛擬線程搭建靜態文件服務器
    主站蜘蛛池模板: 伊人久久大香线蕉综合高清| 扒开校花粉嫩小泬喷潮漫画| 又粗又大又爽又黄的免费视频 | 69国产精品人妻无码免费| caoporm国产精品视频免费| 国产AV午夜精品一区二区入口| 国产骚妇BB网| 女教师の诱惑| 亚洲国产精品无码2019| AV天堂午夜精品蜜臀AV| 国产午夜精品片一区二区三区| 免费成年人在线视频| 亚洲AV无码乱码国产麻豆P| 97精品在线观看| 精品国产90后在线观看| 日本久久和电影| 综合久久久久久久综合网| 国产免费午夜| 日韩高清毛片| 98久久人妻少妇激情啪啪| 极品少妇小泬50PTHEPON| 特级淫片大乳女子高清视频| chinese黑人第一次| 久久亚洲精品永久网站| 羞羞在线观看| 国产精品99亚发布| 日本伦理电影聚| 99国产在线视频有精品视频| 精品视频网站| 亚洲成人免费在线观看| 高H高肉强J短篇NP| 人人超碰97caoporen国产| 97视频在线播放| 老师给美女同学开嫩苞| 伊人久久大香线蕉观看| 火影忍者高清无码黄漫| 性欧美金发洋妞xxxxbbbb| 国产精品久久国产三级国不卡顿 | 免费可以看污动画软件| 亚洲乱码国产乱码精品精98| 国产女合集小岁9三部|