小架構step系列20:請求和響應的擴展點

1 概述

通過上一篇了解請求和響應的流程,Spring在設計上留了不少擴展點。里面通過查找接口的方式獲取的地方,都可以成為一種擴展點,因為只要實現這類接口就可以成為Spring加載的一部分。本文了解一下這些擴展點,方便后面進行擴展。

2 原理

2.1 HandleInterceptor的擴展點

加載HandleInterceptor的方法getInterceptors是在各個HandlerMapping初始化的時候調用的,該方法的調用會加載所有實現了WebMvcConfigurer接口的類,通過WebMvcConfigurer提供的addInterceptors()可以自定義增加HandleInterceptor:

// 繼承關系:EnableWebMvcConfiguration < DelegatingWebMvcConfiguration < WebMvcConfigurationSupport
// EnableWebMvcConfiguration在開啟WebMvc后由Spring觸發加載
// 源碼位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
protected final Object[] getInterceptors(FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {if (this.interceptors == null) {InterceptorRegistry registry = new InterceptorRegistry();// 加載自定義HandleInterceptor,自定義的Interceptor在最前面addInterceptors(registry);registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));this.interceptors = registry.getInterceptors();}return this.interceptors.toArray();
}
// 源碼位置:org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
protected void addInterceptors(InterceptorRegistry registry) {// configurers為WebMvcConfigurerCompositethis.configurers.addInterceptors(registry);
}
// org.springframework.web.servlet.config.annotation.WebMvcConfigurerComposite
public void addInterceptors(InterceptorRegistry registry) {// delegates里注入了所有實現了WebMvcConfigurer接口的類,// 也就是說如果希望自定義一個HandlerInterceptor,則可以通過實現WebMvcConfigurer接口來完成for (WebMvcConfigurer delegate : this.delegates) {delegate.addInterceptors(registry);}
}

2.2 HandlerMethodArgumentResolver擴展點

在初始化RequestMappingHandlerAdapter的時候,就把實現WebMvcConfigurer接口來增加的HandlerMethodArgumentResolver存放到了RequestMappingHandlerAdapter,在RequestMappingHandlerAdapter執行afterPropertiesSet()的時候,再把自定義的HandlerMethodArgumentResolver和默認的一起加到argumentResolvers中:

// RequestMappingHandlerAdapter初始化后,觸發afterPropertiesSet()方法,在里面初始化HandlerMethodArgumentResolver
// 源碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
public void afterPropertiesSet() {// Do this first, it may add ResponseBody advice beansinitControllerAdviceCache();if (this.argumentResolvers == null) {// 加載默認的HandlerMethodArgumentResolverList<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.initBinderArgumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.returnValueHandlers == null) {List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}
}
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);// 創建了二十多個HandlerMethodArgumentResolverresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));resolvers.add(new RequestParamMapMethodArgumentResolver());resolvers.add(new PathVariableMethodArgumentResolver());resolvers.add(new PathVariableMapMethodArgumentResolver());resolvers.add(new MatrixVariableMethodArgumentResolver());resolvers.add(new MatrixVariableMapMethodArgumentResolver());resolvers.add(new ServletModelAttributeMethodProcessor(false));resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));resolvers.add(new RequestHeaderMapMethodArgumentResolver());resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new SessionAttributeMethodArgumentResolver());resolvers.add(new RequestAttributeMethodArgumentResolver());resolvers.add(new ServletRequestMethodArgumentResolver());resolvers.add(new ServletResponseMethodArgumentResolver());resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RedirectAttributesMethodArgumentResolver());resolvers.add(new ModelMethodProcessor());resolvers.add(new MapMethodProcessor());resolvers.add(new ErrorsMethodArgumentResolver());resolvers.add(new SessionStatusMethodArgumentResolver());resolvers.add(new UriComponentsBuilderMethodArgumentResolver());if (KotlinDetector.isKotlinPresent()) {resolvers.add(new ContinuationHandlerMethodArgumentResolver());}// 加載自定義的HandlerMethodArgumentResolver,注意自定義的Resolver是在中間偏后的位置if (getCustomArgumentResolvers() != null) {resolvers.addAll(getCustomArgumentResolvers());}resolvers.add(new PrincipalMethodArgumentResolver());resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));resolvers.add(new ServletModelAttributeMethodProcessor(true));return resolvers;
}
public List<HandlerMethodArgumentResolver> getCustomArgumentResolvers() {// 自定義的HandlerMethodArgumentResolver是在之前賦值的return this.customArgumentResolvers;
}// 源碼位置:org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration
@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcValidator") Validator validator) {// 父類為WebMvcConfigurationSupportRequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager, conversionService, validator);adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());return adapter;
}// 源碼位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcValidator") Validator validator) {RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();adapter.setContentNegotiationManager(contentNegotiationManager);adapter.setMessageConverters(getMessageConverters());adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));// 設置自定義的HandlerMethodArgumentResolveradapter.setCustomArgumentResolvers(getArgumentResolvers());adapter.setCustomReturnValueHandlers(getReturnValueHandlers());if (jackson2Present) {adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));}AsyncSupportConfigurer configurer = getAsyncSupportConfigurer();if (configurer.getTaskExecutor() != null) {adapter.setTaskExecutor(configurer.getTaskExecutor());}if (configurer.getTimeout() != null) {adapter.setAsyncRequestTimeout(configurer.getTimeout());}adapter.setCallableInterceptors(configurer.getCallableInterceptors());adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());return adapter;
}
protected final List<HandlerMethodArgumentResolver> getArgumentResolvers() {if (this.argumentResolvers == null) {this.argumentResolvers = new ArrayList<>();// addArgumentResolvers()接口由DelegatingWebMvcConfiguration實現addArgumentResolvers(this.argumentResolvers);}return this.argumentResolvers;
}// 源碼位置:org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {// configurers為WebMvcConfigurerCompositethis.configurers.addArgumentResolvers(argumentResolvers);
}// 源碼位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurerComposite
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {// delegates里注入了所有實現了WebMvcConfigurer接口的類,// 也就是說如果希望自定義一個HandlerMethodArgumentResolver,則可以通過實現WebMvcConfigurer接口來完成for (WebMvcConfigurer delegate : this.delegates) {delegate.addArgumentResolvers(argumentResolvers);}
}

2.3 RequestMappingHandlerAdapter的擴展點

在初始化RequestMappingHandlerAdapter的時候,就把實現WebMvcConfigurer接口來增加的HandlerMethodReturnValueHandler存放到了RequestMappingHandlerAdapter,在RequestMappingHandlerAdapter執行afterPropertiesSet()的時候,再把自定義的HandlerMethodReturnValueHandler和默認的一起加到returnValueHandlers中:

// RequestMappingHandlerAdapter初始化后,觸發afterPropertiesSet()方法,在里面初始化HandlerMethodReturnValueHandler
// 源碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
public void afterPropertiesSet() {// Do this first, it may add ResponseBody advice beansinitControllerAdviceCache();if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.initBinderArgumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.returnValueHandlers == null) {// 加載默認的HandlerMethodReturnValueHandlerList<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}
}
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);handlers.add(new ModelAndViewMethodReturnValueHandler());handlers.add(new ModelMethodProcessor());handlers.add(new ViewMethodReturnValueHandler());handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));handlers.add(new StreamingResponseBodyReturnValueHandler());handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice));handlers.add(new HttpHeadersReturnValueHandler());handlers.add(new CallableMethodReturnValueHandler());handlers.add(new DeferredResultMethodReturnValueHandler());handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));handlers.add(new ServletModelAttributeMethodProcessor(false));handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice));handlers.add(new ViewNameMethodReturnValueHandler());handlers.add(new MapMethodProcessor());// 加載自定義的HandlerMethodReturnValueHandler,注意自定義的Handler是在中間偏后的位置if (getCustomReturnValueHandlers() != null) {handlers.addAll(getCustomReturnValueHandlers());}if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));}else {handlers.add(new ServletModelAttributeMethodProcessor(true));}return handlers;
}
public List<HandlerMethodReturnValueHandler> getCustomReturnValueHandlers() {// 自定義的HandlerMethodReturnValueHandler是在之前賦值的return this.customReturnValueHandlers;
}// 初始化RequestMappingHandlerAdapter的時候,加載自定義的HandlerMethodReturnValueHandler
// 源碼位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcValidator") Validator validator) {RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();adapter.setContentNegotiationManager(contentNegotiationManager);adapter.setMessageConverters(getMessageConverters());adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));adapter.setCustomArgumentResolvers(getArgumentResolvers());// 設置自定義的RequestMappingHandlerAdapteradapter.setCustomReturnValueHandlers(getReturnValueHandlers());if (jackson2Present) {adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));}AsyncSupportConfigurer configurer = getAsyncSupportConfigurer();if (configurer.getTaskExecutor() != null) {adapter.setTaskExecutor(configurer.getTaskExecutor());}if (configurer.getTimeout() != null) {adapter.setAsyncRequestTimeout(configurer.getTimeout());}adapter.setCallableInterceptors(configurer.getCallableInterceptors());adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());return adapter;
}
protected final List<HandlerMethodReturnValueHandler> getReturnValueHandlers() {if (this.returnValueHandlers == null) {this.returnValueHandlers = new ArrayList<>();// addReturnValueHandlers()接口由DelegatingWebMvcConfiguration實現addReturnValueHandlers(this.returnValueHandlers);}return this.returnValueHandlers;
}// 源碼位置:org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {// configurers為WebMvcConfigurerCompositethis.configurers.addReturnValueHandlers(returnValueHandlers);
}// 源碼位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurerComposite
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {for (WebMvcConfigurer delegate : this.delegates) {// delegates里注入了所有實現了WebMvcConfigurer接口的類,// 也就是說如果希望自定義一個RequestMappingHandlerAdapter,則可以通過實現WebMvcConfigurer接口來完成delegate.addReturnValueHandlers(returnValueHandlers);}
}

2.4 MessageConverter的擴展點

在初始化RequestMappingHandlerAdapter的時候,就把實現WebMvcConfigurer接口來增加的MessageConverter存放到了RequestMappingHandlerAdapter,在RequestMappingHandlerAdapter執行afterPropertiesSet()的時候,再把自定義的MessageConverter加到messageConverters中:

// RequestMappingHandlerAdapter初始化后,觸發afterPropertiesSet()方法,在里面初始化HandlerMethodReturnValueHandler
// 源碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
public void afterPropertiesSet() {// Do this first, it may add ResponseBody advice beansinitControllerAdviceCache();if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.initBinderArgumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.returnValueHandlers == null) {// 加載默認的HandlerMethodReturnValueHandlerList<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}
}
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);handlers.add(new ModelAndViewMethodReturnValueHandler());handlers.add(new ModelMethodProcessor());handlers.add(new ViewMethodReturnValueHandler());// 有設置MessageConverterhandlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));handlers.add(new StreamingResponseBodyReturnValueHandler());// 有設置MessageConverterhandlers.add(new HttpEntityMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice));handlers.add(new HttpHeadersReturnValueHandler());handlers.add(new CallableMethodReturnValueHandler());handlers.add(new DeferredResultMethodReturnValueHandler());handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));handlers.add(new ServletModelAttributeMethodProcessor(false));// 有設置MessageConverterhandlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice));handlers.add(new ViewNameMethodReturnValueHandler());handlers.add(new MapMethodProcessor());if (getCustomReturnValueHandlers() != null) {handlers.addAll(getCustomReturnValueHandlers());}if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));}else {handlers.add(new ServletModelAttributeMethodProcessor(true));}return handlers;
}
public List<HttpMessageConverter<?>> getMessageConverters() {// 自定義的MessageConverter是在之前賦值的return this.messageConverters;
}// 初始化RequestMappingHandlerAdapter的時候,加載自定義的MessageConverter
// 源碼位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcValidator") Validator validator) {RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();adapter.setContentNegotiationManager(contentNegotiationManager);// 設置自定義的MessageConverteradapter.setMessageConverters(getMessageConverters());adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));adapter.setCustomArgumentResolvers(getArgumentResolvers());adapter.setCustomReturnValueHandlers(getReturnValueHandlers());if (jackson2Present) {adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));}AsyncSupportConfigurer configurer = getAsyncSupportConfigurer();if (configurer.getTaskExecutor() != null) {adapter.setTaskExecutor(configurer.getTaskExecutor());}if (configurer.getTimeout() != null) {adapter.setAsyncRequestTimeout(configurer.getTimeout());}adapter.setCallableInterceptors(configurer.getCallableInterceptors());adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());return adapter;
}
protected final List<HttpMessageConverter<?>> getMessageConverters() {if (this.messageConverters == null) {this.messageConverters = new ArrayList<>();// 配置自定義的MessageConverterconfigureMessageConverters(this.messageConverters);if (this.messageConverters.isEmpty()) {addDefaultHttpMessageConverters(this.messageConverters);}extendMessageConverters(this.messageConverters);}return this.messageConverters;
}// 源碼位置:org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {// configurers為WebMvcConfigurerCompositethis.configurers.configureMessageConverters(converters);
}// 源碼位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurerComposite
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {for (WebMvcConfigurer delegate : this.delegates) {// delegates里注入了所有實現了WebMvcConfigurer接口的類,// 也就是說如果希望自定義一個MessageConverter,則可以通過實現WebMvcConfigurer接口來完成delegate.configureMessageConverters(converters);}
}

3 架構一小步

  • Controller接口處理前后要預先或者做后處理時用HandleInterceptor擴展。
  • Controller接口處理特殊參數的時候用HandlerMethodArgumentResolver擴展。
  • 請求返回值格式處理可以通過MessageConverter擴展。
  • 整個請求的處理如果需要替換則用RequestMappingHandlerAdapter擴展,這個除非有特殊請求,否則不推薦。
上面這些擴展都通過繼承WebMvcConfigurer來增加擴展類。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/91956.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/91956.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/91956.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

模型材質一鍵替換~輕松還原多種三維場景

1. 概述模型的材質決定了三維場景的整體視效&#xff0c;山海鯨可視化不僅支持模型材質的替換與編輯&#xff0c;而且提供了大量現成的模型材質供大家使用&#xff0c;能夠幫助大家實現更高效的三維場景搭建。模型材質主要分為PBR材質和水面材質兩個部分。其中大部分靜態模型都…

【JS逆向基礎】數據庫之mysql

前言&#xff1a;mysql數據庫管理系統&#xff0c;由瑞典MySQL AB 公司開發&#xff0c;目前屬于 Oracle 旗下公司。MySQL 最流行的關MySQL是一個開源免費的關系型數據庫管系型數據庫管理系統&#xff0c;在 WEB 應用方面ySQL是最好的 RDBMS (Relational Database Management S…

金融工程、金融與經濟學知識點

本文整理了20個金融工程、金融和經濟學知識點及邏輯&#xff0c;這些是理解金融市場運作和進行量化分析的基石。 1. 金融工程 - 遠期與期權&#xff08;Forward & Option&#xff09;的定價與風險管理 遠期定價&#xff1a; 利用無套利原則&#xff0c;遠期合約的價格應等…

Vue 3 中導出 Excel 文件

在 Vue 3 中導出 Excel 文件&#xff0c;通常可以使用一些流行的 JavaScript 庫&#xff0c;如 SheetJS (xlsx) 或者 exceljs。這里我將分別介紹如何使用這兩個庫來在 Vue 3 應用中導出 Excel 文件。方法 1&#xff1a;使用 SheetJS (xlsx)安裝 SheetJS首先&#xff0c;你需要安…

奇麟大數據:前端大文件上傳解決方案

在奇麟大數據業務系統的開發及使用過程中&#xff0c;例如OBS對象存儲文件管理、流計算DSC依賴管理&#xff0c;經常會遇到上傳文件這樣的基礎需求&#xff0c;一般情況下&#xff0c;前端上傳文件就是new FormData&#xff0c;然后把文件 append 進去&#xff0c;然后post發送…

立創EDA中雙層PCB疊層分析

立創EDA中雙層PCB疊層分析 結論&#xff1a;立創EDA中的雙層 PCB 疊層視圖相比傳統視圖&#xff0c;多出一個焊盤層&#xff08;博主命名&#xff09;&#xff1b; 1. 傳統雙層 PCB 疊層示意圖 絲印層 印刷元件標識、極性標記及廠商信息 輔助組裝與后期維護 阻焊層 覆蓋銅層表…

深入理解進程:從底層原理到硬件系統實戰

深入理解進程&#xff1a;從底層原理到嵌入式實戰&#xff08;3-4 萬字詳解&#xff09; 前言&#xff1a;為什么硬件開發者必須吃透進程&#xff1f; 作為嵌入式開發者&#xff0c;你可能會說&#xff1a;“我平時用的 RTOS 里只有任務&#xff08;Task&#xff09;&#xff0…

Elasticsearch 簡化指南:GCP Google Compute Engine

作者&#xff1a;來自 Elastic Eduard Martin 系列內容的一部分&#xff1a;開始使用 Elasticsearch&#xff1a;GCP 想獲得 Elastic 認證&#xff1f;看看下一期 Elasticsearch Engineer 培訓什么時候開始&#xff01; Elasticsearch 擁有豐富的新功能&#xff0c;幫助你根據…

STM32的定時器輸入捕獲-超聲波測距案例

STM32的定時器輸入捕獲-超聲波測距案例 gitee代碼輸入捕獲硬件電路案例說明主函數代碼 gitee代碼 https://gitee.com/xiaolixi/l-stm32/tree/master/STM32F103C8T6/2-1tem-ld-timer-input-pluse 輸入捕獲硬件電路 超聲波測距案例說明 使用超聲波測距傳感器使用tim1的輸入捕獲…

[特殊字符] Spring Boot 常用注解全解析:20 個高頻注解 + 使用場景實例

一文掌握 Spring Boot 中最常用的 20 個注解&#xff0c;涵蓋開發、配置、Web、數據庫、測試等場景&#xff0c;配合示例講解&#xff0c;一站式掌握&#xff01;&#x1f4cc; 一、核心配置類注解 1. SpringBootApplication 作用&#xff1a;標記為 Spring Boot 應用的入口類&…

【工具變量】地級市城市包容性綠色增長數據(2011-2023年)

城市包容性綠色增長是指在推動城市經濟增長的過程中&#xff0c;兼顧環境可持續性、社會公平和包容性發展的理念與實踐。它強調在實現綠色轉型和低碳發展的同時&#xff0c;保障社會各群體&#xff0c;特別是弱勢群體的利益與參與權利&#xff0c;確保增長成果能夠公平共享 本…

深入理解React Hooks:從使用到原理

4. 源碼解析類:《深入理解React Hooks:從使用到原理》 # 深入理解React Hooks:從使用到原理?? **背景**: - Hooks解決了Class組件的哪些問題? - 為什么不能在循環/條件中調用Hooks??? **核心原理**:### 1. Hooks鏈表 React內部維護一個單向鏈表:fiber.memoizedSta…

【云原生】Docker 部署 Elasticsearch 9 操作詳解

目錄 一、前言 二、Elasticsearch 9 新特性介紹 2.1 基于 Lucene 10 重大升級 2.2 Better Binary Quantization(BBQ) 2.3 Elastic Distributions of OpenTelemetry(EDOT) 2.4 LLM 可觀測性 2.5 攻擊發現與自動導入 2.6 ES|QL 增強 2.7 語義檢索 三、基于Docker部署…

uview-ui使用u-search搜索框

1、效果圖 2、帶地址搜索框&#xff0c;在微信小程序線上需要開啟地圖定位接口&#xff0c;若沒有權限則顯示不了城市名&#xff0c;注意事項參考uniapp相關地圖 API調用-CSDN博客 <template><view><u-sticky offset-top"-1"><u-search v-mode…

Elasticsearch+Logstash+Kibana部署

目錄 一、實驗準備 1.下載安裝 2.下載java 2.同步主機系統時間 二、部署 1.部署elasticsearch 修改 /etc/elasticsearch/elasticsearch.yml 配置文件 修改 /etc/hosts/ 文件 啟動elasticsearch 查看是否啟動進程netstat -antptu | grep java 2.部署logstash 進入/et…

TEngine學習

關于靜態類中的靜態變量賦值&#xff1a; public static class ActorEventDefine{public static readonly int ScoreChange RuntimeId.ToRuntimeId("ActorEventDefine.ScoreChange");public static readonly int GameOver RuntimeId.ToRuntimeId("ActorEventD…

獵板:在 5G 與 AI 時代,印制線路板如何滿足高性能需求

5G 與 AI 技術的深度融合&#xff0c;推動電子設備向高速傳輸、高算力、高集成方向發展&#xff0c;印制線路板&#xff08;PCB&#xff09;作為核心載體&#xff0c;其性能直接決定終端設備的運行效率與可靠性。獵板 PCB 聚焦 5G 通信的高頻需求與 AI 算力的密集需求&#xff…

教你如何借助AI精讀文獻

目錄1. 原文2. 對文獻有一個快速的理解3. 專業術語解讀4. 解答疑問5. 借助AI翻譯摘要和引言部分5.1 **摘要 (Abstract)**5.2 **引言 (Introduction)**6. 介紹論文中的“Stack-Propagation”7. 查閱論文里的參考文獻&#xff0c;看看他是如何在Introduction中引述研究進展文獻&a…

FastAdmin框架超級管理員密碼重置與常規admin安全機制解析-卓伊凡|大東家

FastAdmin框架超級管理員密碼重置與常規admin安全機制解析-卓伊凡|大東家我們可以看到admin賬戶是不允許直接修改的&#xff0c;這也是目前fastadmin 框架不允許的&#xff0c;那么如何處理一、FastAdmin超級管理員密碼重置方法當FastAdmin的超級管理員密碼忘記或需要重置時&am…

我做的基礎服務項目,是如何實現 API 安全與限流的(短信、郵件、文件上傳、釘釘通知)

我做的基礎服務項目&#xff0c;是如何實現 API 安全與限流的&#xff08;短信、郵件、文件上傳、釘釘通知&#xff09;一、背景 最近我做了一個基礎服務項目&#xff0c;主要對外提供短信、郵件、文件上傳和釘釘通知等基礎功能。這些接口是多個業務系統都要調用的&#xff0c;…