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