springmvc5.x-mvc实现原理及源码实现

上文:spring5.x-声明式事务原理及源码实现


系列文章:

                    spring5.x-声明式事务原理及源码实现

                    spring5.x-AOP实现原理及源码分析

                    spring5.x-监听器原理及源码实现

                    spring5.x-解决循环依赖分析

                    spring5.x-IOC模块源码学习

                    spring5.x介绍及搭配spring源码阅读环境


基础知识

请看原来写的文章:springmvc

源码学习

cca5a76aada179e072014928521a51f5.png

@RequestMapping("/{id}")public String showUserInfo(ModelMap modelMap, @PathVariable("id")Integer id){Student student = new Student();student.setId(id);student.setAge(100);student.setName("test");modelMap.addAttribute("name", student.getName());modelMap.addAttribute("age", student.getAge());modelMap.addAttribute("id", student.getId());return "result";}

springmvc的初始化

  1. DispatcherServlet 初始化:DispatcherServlet 是 Spring MVC 的前端控制器,在 web.xml 或 WebApplicationInitializer 中配置 Servlet 容器时会初始化 DispatcherServlet。DispatcherServlet 的初始化源码位置为 org.springframework.web.servlet.DispatcherServlet。

<servlet><servlet-name>springDispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet>

代码位置:org.springframework.web.servlet.DispatcherServlet#initStrategies

//用于初始化 Spring MVC 的各个策略组件。
protected void initStrategies(ApplicationContext context) {//初始化处理文件上传的解析器,用于解析请求中的 multipart 数据。this.initMultipartResolver(context);//初始化处理国际化的解析器,用于解析请求中的语言区域信息。this.initLocaleResolver(context);//初始化处理主题的解析器,用于解析请求中的主题信息。this.initThemeResolver(context);//初始化处理器映射器,用于将请求映射到对应的处理器(Controller)。this.initHandlerMappings(context);//初始化处理器适配器,用于调用处理器的方法并从中获取 ModelAndView 对象。this.initHandlerAdapters(context);//初始化处理器异常解析器,用于处理请求过程中发生的异常。this.initHandlerExceptionResolvers(context);//初始化请求到视图名称的转换器,用于将处理器返回的逻辑视图名称转换为实际的视图路径。this.initRequestToViewNameTranslator(context);//初始化视图解析器,用于将视图名称解析为具体的视图类型。this.initViewResolvers(context);//初始化 FlashMap 管理器,用于处理请求间的数据传递。this.initFlashMapManager(context);}

dc032971efa1ce353e8c28da77fc3fe8.png

  1. 注册 ServletContextListener:在 Servlet 容器启动时,通常会注册一个监听器(ServletContextListener)来初始化 Spring MVC 的上下文。在监听器的 contextInitialized 方法中实现 Spring MVC 的初始化,例如加载配置文件、创建 ApplicationContext 等。

代码位置:org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener

//执行应用程序事件监听器
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {//调用监听器的 并传递当前发生的应用程序事件作为参数。listener.onApplicationEvent(event);} catch (ClassCastException var6) {String msg = var6.getMessage();if (msg != null && !this.matchesClassCastMessage(msg, event.getClass().getName())) {throw var6;}Log logger = LogFactory.getLog(this.getClass());if (logger.isDebugEnabled()) {logger.debug("Non-matching event type for listener: " + listener, var6);}}}
  1. WebApplicationContext 初始化:Spring MVC 使用了自己的容器(WebApplicationContext),该容器会在 ServletContextListener 初始化时创建并配置。关于 WebApplicationContext 的初始化可以参考 org.springframework.web.context.support.XmlWebApplicationContext 或 org.springframework.web.context.support.AnnotationConfigWebApplicationContext 源码。

da408f84bedb1e065eb92149bcf7dc26.png

  1. HandlerMapping 和 HandlerAdapter 初始化:HandlerMapping 负责将请求映射到对应的处理器(Controller),而 HandlerAdapter 则负责调用相应的处理器方法。这些组件的初始化涉及配置文件、注解扫描等过程,相关源码位置为 org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping、org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping、org.springframework.web.servlet.handler.SimpleUrlHandlerMapping,以及 org.springframework.web.servlet.handler.BeanNameUrlHandlerAdapter、org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter 等。

90d762452f9c9b0ad1331f787b3c0601.pnga02ec54c3a625cdd83c7e76f1bb511f0.png

注意监听器:org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener

1e32edea4a64e084cb4983a003009cbb.png

  1. 视图解析器初始化:视图解析器(ViewResolver)负责将处理器方法的返回值解析为具体的视图。Spring MVC 支持多种类型的视图解析器,如 InternalResourceViewResolver、FreeMarkerViewResolver 等。初始化过程中会配置视图解析器的相关属性和位置,例如视图前缀、后缀等。相关源码位置为 org.springframework.web.servlet.view.InternalResourceViewResolver。

由于我们配的视图是:

<!-- 配置视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/views/"></property><property name="suffix" value=".jsp"></property></bean>

所以解析出来是:

代码位置:org.springframework.beans.factory.BeanFactoryUtils#beansOfTypeIncludingAncestors(org.springframework.beans.factory.ListableBeanFactory, java.lang.Class<T>, boolean, boolean)

5e453c7e2e35df940479ed79abea1754.png

最终我们都会调onRefresh()完成初始化。代码位置:org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

if (!this.refreshEventReceived) {// Either the context is not a ConfigurableApplicationContext with refresh// support or the context injected at construction time had already been// refreshed -> trigger initial onRefresh manually here.onRefresh(wac);}

springmvc分发实现

那么spring加载完成后,就是调用的问题,这里注意,会根据不同的调用方式来进行分发,比如http tcp 等的分发方式都不太一样。那最终都会调到doDispatch.

代码位置:org.springframework.web.servlet.DispatcherServlet#doDispatch

//分发方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {//初始化请求信息HttpServletRequest processedRequest = request;//用于存储根据请求对象匹配到的处理器对象HandlerExecutionChain mappedHandler = null;//标识是否已解析多部分请求boolean multipartRequestParsed = false;//获取当前WebAsyncManager对象(异步处理)WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {//初始化视图模型ModelAndView mv = null;Exception dispatchException = null;try {//检查请求是否为多部分请求(文件或),通过检查请求头中的 "Content-Type" 是否以 "multipart/" 开头来判断。processedRequest = checkMultipart(request);//判断是否一致,如果是则为truemultipartRequestParsed = (processedRequest != request);// 获取处理对象mappedHandler = getHandler(processedRequest);//为空就是没找着路劲 返回404if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 获取处理对HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 获取请求方法String method = request.getMethod();//判断是否为get请求boolean isGet = "GET".equals(method);//如果是 或头为HEADif (isGet || "HEAD".equals(method)) {//获取最后时间long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (logger.isDebugEnabled()) {logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);}//新请一个响应并检查,如果不通过直接中止if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}//调用处理器的预处理方法 ,如果不通过中止if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 调用适配器mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//检查异步处理是否已经开始if (asyncManager.isConcurrentHandlingStarted()) {return;}//设置默认的视图名到ModelAndView中applyDefaultViewName(processedRequest, mv);//调用后置处理器方法mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.//如果出象则创建一个错误的异常dispatchException = new NestedServletException("Handler dispatch failed", err);}//返回客户端processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {// 方法检查异步处理是否已经开始if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {//用于在异步处理开始后执行相关的清理操作或其他逻辑处理mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {//用于清理多部分请求中使用的资源// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}

15bdc30486a7e6764ddc821df1631bf7.png

接着细节深入

processedRequest = checkMultipart(request);

代码位置:org.springframework.web.servlet.DispatcherServlet#checkMultipart

//将请求转换为分段请求,并使分段解析程序可用。如果未设置多部分解析程序,则只需使用现有请求。
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {//不为空 且 请求是否是multipart类型if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {//将请求转换为 multipart类型的请求对象,if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +"this typically results from an additional MultipartFilter in web.xml");}//检查当前请求是否已经发生过多部分请求解析失败的异常else if (hasMultipartException(request) ) {logger.debug("Multipart resolution failed for current request before - " +"skipping re-resolution for undisturbed error rendering");}else {try {//转换为HttpServletRequest 并返回return this.multipartResolver.resolveMultipart(request);}catch (MultipartException ex) {if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {logger.debug("Multipart resolution failed for error dispatch", ex);// Keep processing error dispatch with regular request handle below}else {throw ex;}}}}// If not returned before: return original request.return request;}

上面这个方法解析multipart类型的请求。

接着:mappedHandler = getHandler(processedRequest); 这个是用于确定当前请求的处理程

Nullableprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {//不为空if (this.handlerMappings != null) {//循环调用每个 HandlerMapping 的 getHandler(request) 方法,传入当前的 HttpServletRequest 对象作为参数,来获取对应的处理器。for (HandlerMapping hm : this.handlerMappings) {if (logger.isTraceEnabled()) {logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");}HandlerExecutionChain handler = hm.getHandler(request);if (handler != null) {return handler;}}}return null;}

当面这个用于获取HandlerExecutionChain,其实就是请求处理器(Handler)的方法。

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

根据刚刚获取的处理器进行获取HandlerAdapter,其实就是决定用来调modelandview或其他视图,有很多种,比如:

  1. RequestMappingHandlerAdapter:适配处理器函数、带有注解的控制器等类型的处理器。

  2. SimpleControllerHandlerAdapter:适配基于实现 Controller 接口的控制器。

  3. HttpRequestHandlerAdapter:适配实现 HttpRequestHandler 接口的处理器,用于处理原始的 HttpServletRequest 与 HttpServletResponse。

  4. HandlerAdapter 的默认实现 DefaultHandlerAdapter:用于处理没有明确适配器的处理器类型,默认使用 ServletInvocableHandlerMethod 来执行处理器方法。

  5. 针对异步请求的适配器:例如 AsyncHandlerInterceptor 和 Callable 所对应的适配器。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {//循环判断是哪种类型匹配,匹配就返回for (HandlerAdapter ha : this.handlerAdapters) {if (logger.isTraceEnabled()) {logger.trace("Testing handler adapter [" + ha + "]");}//获取最终的HandlerAdapterif (ha.supports(handler)) {return ha;}}}throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");}

mappedHandler.applyPreHandle(processedRequest, response),这个方法用于记录拦截器的执行位置。

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = 0; i < interceptors.length; i++) {HandlerInterceptor interceptor = interceptors[i];//调用前置拦截器方法 如果返回值为 false,表示拦截器不允许继续执行后续的处理逻辑if (!interceptor.preHandle(request, response, this.handler)) {//方法进行拦截器链的后置处理,并直接返回 falsetriggerAfterCompletion(request, response, null);return false;}//记录位置this.interceptorIndex = i;}}return true;}
//用于触发拦截器链的后置处理(afterCompletion
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)throws Exception {HandlerInterceptor[] interceptors = getInterceptors();//不为空if (!ObjectUtils.isEmpty(interceptors)) {//循环执行拦截器的后置处理逻辑,通常用于资源清理、日志记录等操作。for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];try {interceptor.afterCompletion(request, response, this.handler, ex);}catch (Throwable ex2) {logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}}}

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

接受三个参数:processedRequest 是经过前置处理器链处理后的请求对象,response 是响应对象,mappedHandler.getHandler() 是映射到的请求处理器对象。

在执行 handle() 方法时,会根据请求处理器的类型调用相应的处理逻辑。不同的请求处理器可能是不同类型的对象,例如 Controller、HttpRequestHandler 或 HttpMessageConverter 等。

注意:一般HTTP 请求中可以包含多种类型的参数,常见的有以下几种类型:

  1. 查询参数(Query Parameters):位于 URL 中,以 ? 开头,键值对使用 key=value 的形式表示,多个参数之间使用 & 分隔。例如: http://hong.com/api?key1=value1&key2=value2。可以通过解析 URL 来获取查询参数。

  2. 路径参数(Path Parameters):位于 URL 路径中,用于表示特定资源的标识符或属性。路径参数通常用于 RESTful 风格的路由中,使用占位符来代表参数值。例如: http://hong.com/api/users/{userId},其中 {userId} 就是路径参数。

  3. 请求体参数(Request Body Parameters):位于请求体中,通常使用表单数据或 JSON 格式来传递。可以通过 HTTP 请求的 Content-Type 头部字段来确定参数的类型。常见的参数类型有:

    • 表单参数(Form Parameters):使用表单数据格式传递,即 key=value 的形式。

    • JSON 参数(JSON Parameters):使用 JSON 格式传递,请求体中的数据是一个合法的 JSON 对象。

    • 文件参数(File Parameters):用于上传文件,请求体中包含文件的二进制数据。

判断请求参数的方式取决于你使用的服务器端框架或编程语言。大多数框架提供了相应的工具或库来解析和获取请求参数。一般而言,可以通过从请求对象中获取相应的参数来获取请求参数。例如,在 Java 的 Spring 框架中,可以使用 @RequestParam 注解、HttpServletRequest 对象等来获取请求参数。

这个位置非常复杂。有兴趣可以深入。

那么有些同学会疑问,springmvc可以支持哪些参数?

  1. 查询参数(Query Parameters):将查询参数作为方法参数进行接收。可以使用 @RequestParam 注解将参数与请求中的查询参数绑定,还可以指定默认值、是否必需等属性。

  2. 路径参数(Path Parameters):通过在请求路径中使用占位符来接收参数。使用 @PathVariable 注解将路径参数与方法参数进行绑定。

  3. 请求体参数(Request Body Parameters):通常用于接收 POST 或 PUT 请求中的数据。可以使用 @RequestBody 注解将请求体中的数据绑定到方法参数上。支持的数据格式包括 JSON、XML 等。

  4. 头部信息(Request Header):可以使用 @RequestHeader 注解将特定的请求头信息与方法参数绑定。

  5. Cookie 参数(Cookie Parameters):使用 @CookieValue 注解将特定的 Cookie 值与方法参数进行绑定。

  6. 表单参数(Form Parameters):适用于接收表单提交的参数。可以使用 @RequestParam 注解或 @ModelAttribute 注解将表单字段与方法参数进行绑定。

  7. 文件上传(File Upload):接收文件上传请求时,可以使用 MultipartFile 类型的方法参数来接收上传的文件数据。

当然上面是我所看源码了解到的,目前有没有其它暂时没看到。可以HandlerMethodArgumentResolver

6cf5361b22eadd6ab088a4d983c577a0.png

mappedHandler.applyPostHandle(processedRequest, response, mv);

上面这段是拦截器的一些实现,我们有些请求或不开放的接口权限等可以结合这个来进行拦截。

代码位置:org.springframework.web.servlet.HandlerExecutionChain#applyPostHandle

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = interceptors.length - 1; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];//调用拦截器interceptor.postHandle(request, response, this.handler, mv);}}}

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

这个是最后的视图的实现了,但是视图有好多种 如下:

  1. JSP 视图(InternalResourceView):使用 JSP(JavaServer Pages)作为视图技术。通过 InternalResourceViewResolver 视图解析器,将逻辑视图名映射到 JSP 文件,并将模型数据传递给 JSP 进行渲染。

  2. Thymeleaf 视图(ThymeleafView):使用 Thymeleaf 模板引擎进行视图渲染。Thymeleaf 是一个现代化的 Java 模板引擎,可以与 HTML、XML、JavaScript 等文件进行集成。

  3. Freemarker 视图(FreeMarkerView):使用 FreeMarker 模板引擎进行视图渲染。FreeMarker 是一个模板引擎,通过模板文件和数据模型生成最终的输出。

  4. Velocity 视图(VelocityView):使用 Apache Velocity 模板引擎进行视图渲染。Velocity 是一个基于 Java 的模板引擎,可用于生成文本、HTML、XML 等格式的输出。

  5. JSON 视图(MappingJackson2JsonView):将模型数据以 JSON 格式返回给客户端。通过 Jackson 库将模型数据序列化为 JSON 字符串,并通过 HttpServletResponse 返回给客户端。

  6. XML 视图(MarshallingView):将模型数据以 XML 格式返回给客户端。通过 JAXB(Java Architecture for XML Binding)将模型数据转换为 XML,并通过 HttpServletResponse 返回给客户端。

  7. 等等:除了上述视图以外,Spring 还支持自定义视图解析器和自定义视图类型,可以根据业务需求使用其他视图技术来进行视图渲染。

代码位置:org.springframework.web.servlet.DispatcherServlet#processDispatchResult

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;//不为空if (exception != null) {//匹配类型为ModelAndViewDefiningExceptionif (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);//转换成ModelAndViewDefiningExceptionmv = ((ModelAndViewDefiningException) exception).getModelAndView();}else {//获取自定义异常Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);//转换mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}// 不为空且 非被清除if (mv != null && !mv.wasCleared()) {//进行视图创建render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else { //证明被解析过了//打印日志if (logger.isDebugEnabled()) {logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +"': assuming HandlerAdapter completed request handling");}}//若请求是异步处理的(Concurrent handling started during a forward),则直接返回,不做后续处理。if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Concurrent handling started during a forwardreturn;}//不为空打印日志if (mappedHandler != null) {mappedHandler.triggerAfterCompletion(request, response, null);}}

后面就是一些异常和finally的处理,都是清空缓存的一些处理。不细看,有兴趣可以了解一下。

最后

springmvc非常重要,特别源码这块,涉及视图解析以及如何拦截等逻辑,这些核心特别是想在spring方面有所提升的同学,建议可以再细详深入debug一行一行把核心逻辑过一下,真的后面想走得深入或做架构方面及整合一些框架这些流程先后顺序必须懂,否则很容易吃一大亏。当然以上仅是本人看法。

参考文章:

https://www.yii666.com/blog/452442.html

https://blog.csdn.net/weixin_56644618/article/details/127594065

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/122415.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GoT:用大语言模型解决复杂的问题

GoT&#xff1a;用大语言模型解决复杂的问题 摘要介绍背景和符号表示语言模型和上下文学习Input-Output&#xff08;IO&#xff09;Chain of thought&#xff08;CoT&#xff09;Multiple CoTTree of thoughts&#xff08;ToT&#xff09; GoT框架推理过程思维变换聚合变换&…

群晖 DS918通过CISCO SG250 LACP 链路聚合效果不佳的问题解决

问题表现 使用的是CISCO交换机打开LACP 链路聚合&#xff0c;且DS918上完成接口聚合并配置为平衡TCP模式后。通过IPREF测速整体网络性能仅能达到300Mbps左右。 问题解决 检查CISCO交换机LAG配置中&#xff0c;针对DS918的接口组是否正确配置了流量配置。请按照如下图所示&#…

无涯教程-Android Intent Standard Extra Data函数

下表列出了各种重要的Android Intent Standard Extra Data。您可以查看Android官方文档以获取额外数据的完整列表- Sr.NoExtra Data & Description1 EXTRA_ALARM_COUNT 用作AlarmManager intents(意图)中的int Extra字段,以告诉正在调用的应用程序intents(意图)释放了多少…

使用Android原生制作毛玻璃效果图片

毛玻璃效果&#xff0c;也被称为模糊效果&#xff0c;是许多现代应用中流行的一种视觉效果。在 Android 中&#xff0c;我们可以通过多种方式实现该效果。本文将探讨如何使用 Android 原生的 Bitmap 类和 RenderScript 来实现毛玻璃效果。 1. 准备工作 首先&#xff0c;你需要…

NIFI关于Parameter Contexts的使用

说明 nifi版本&#xff1a;1.23.2&#xff08;docker镜像&#xff09; 作用 Parameter Contexts&#xff08;参数上下文&#xff09;&#xff1a;参数上下文由 NiFi 实例全局定义/访问。访问策略可以应用于参数上下文&#xff0c;以确定哪些用户可以创建它们。创建后&#x…

什么是RTC

参考&#xff1a; https://zhuanlan.zhihu.com/p/377100294 RTC&#xff08;Real time communication&#xff09;实时通信&#xff0c;是实时音视频的一个简称&#xff0c;我们常说的RTC技术一般指的是WebRTC技术&#xff0c;已经被 W3C 和 IETF 发布为正式标准。由于几乎所…

【数据结构】堆的基础功能实现与PriorityQueue

文章目录 &#x1f340;堆的插入与删除&#x1f6eb;堆的插入&#x1f6a9;代码实现&#xff1a; &#x1f6ec;堆的删除 &#x1f38b;堆的常见习题&#x1f388;习题一&#x1f388;习题二&#x1f388;习题三 &#x1f384;PriorityQueue&#x1f431;‍&#x1f453;Priori…

【LeetCode】202. 快乐数 - hash表 / 快慢指针

目录 2023-9-5 09:56:152023-9-6 19:40:51 202. 快乐数 2023-9-5 09:56:15 关键是怎么去判断循环&#xff1a; hash表&#xff1a; 每次生成链中的下一个数字时&#xff0c;我们都会检查它是否已经在哈希集合中。 如果它不在哈希集合中&#xff0c;我们应该添加它。如果它在…

基于平台快速搭建企业信息化系统的低代码PaaS平台源码

低代码快开平台是一种快速开发应用系统的工具&#xff0c;用户通过少量代码甚至不写代码就可以快速构建出各种应用系统。 随着信息化技术的发展&#xff0c;企业对信息化开发的需求正在逐渐改变&#xff0c;传统的定制开发已经无法满足企业需求。低代码开发平台&#xff0c;让…

C++——STL容器【map和set】

文档&#xff1a;map、set 文章目录 &#x1f36f;1. 关联式容器&#x1fad6;2. set&#x1f37c;1. 模板参数&#x1f37c;2. 构造函数&#x1f37c;3. 修改&#x1f37c;4.操作&#x1f95b;find&#x1f95b;count&#x1f95b;lower_bound & upper_bound & equal_…

Java运算符

目录 一、运算符的概念 二、算数运算符 三、关系运算符 五、逻辑运算符 六、移位运算符 七、条件运算符 八、运算符的优先级 一、运算符的概念 计算机的最基本的用途之一就是执行数学运算&#xff0c;比如: 上述 和 < 等就是运算符&#xff0c;即&#xff1a;对操作…

2023年MySQL实战核心技术第二篇

目录 五 . 日志系统&#xff1a;一条SQL更新语句是如何执行的&#xff1f; 5.1 解释 5.2 重要的日志模块&#xff1a;redo log 5.2.1 解释 5.2.2 WAL&#xff08;Write-Ahead Logging&#xff09; 5.2.3 crash-safe。 5.3 重要的日志模块&#xff1a;binlog 5.3 .1 为什么会有…

存储数据恢复- raid5多块硬盘出现坏道的数据恢复案例

存储数据恢复环境&#xff1a; 某单位一台存储&#xff0c;1个机头4个扩展柜&#xff0c;有两组分别由27块和23块硬盘组建的RAID5阵列。其中由27块磁盘组建的那一组RAID5阵列崩溃&#xff0c;这组RAID5阵列存放是Oracle数据库文件。存储系统上层共划分了11个卷。 存储故障&…

利用Hutool+自定义注解实现数据脱敏

利用Hutool自定义注解实现数据脱敏 前言 我们在使用手机银行的时候经常能看到APP上会将银行卡的卡号中间部分给隐藏掉使用 ***** 来代替&#xff0c;在某些网站上查看一些业务密码时&#xff08;例如签到密码等&#xff09;也会使用 ***** 来隐藏掉真正的密码&#xff0c;那么…

重磅! AIFS+MLOps两大AI基核技术前沿洞察报告出炉!

近日&#xff0c;中国国际服务贸易交易会&#xff08;以下简称“服贸会”&#xff09;在京盛大举办&#xff0c;在服贸会“2023中国AIGC创新发展论坛”和“2023中国金融科技论坛”两大论坛的成果发布环节&#xff0c;九章云极DataCanvas公司与IDC重磅发布「AIFS人工智能基础软件…

linux安装minio以及springboot整合使用

文章目录 1.linux安装minio2.springboot整合minio使用 1.linux安装minio 1.新建文件夹 mkdir /home/minio # 数据文件夹 mkdir /home/minio/data # 创建日志文件夹 mkdir /home/minio/log2.进入文件夹 cd /home/minio3.下载minio&#xff0c;链接可从官网获取 wget https://…

.net core 上传文件大小限制

微软官网文档中给的解释是.net core 默认上传文件大小限制是30M&#xff0c;所以即便你项目里没有限制&#xff0c;这里也有个默认限制。 官网链接地址 总结了一下解决办法&#xff1a; 1.首先项目里添加一个web.config自定义配置文件 在配置文件中加上这段配置 <!--//…

MySql学习笔记03——DQL(数据查询)基本命令

DQL 导入数据 首先使用use database进入数据库中&#xff0c;然后使用命令 source D:\mysql_learning\mysql_learning\document\bjpowernode.sql注意文件名不能有双引号&#xff0c;命令结尾没有分号。 SQL脚本 .sql文件是SQL脚本文件&#xff0c;它里面的内容都是SQL语句…

【数学建模】数据预处理

为什么需要数据预处理 数学建模是将实际问题转化为数学模型来解决的过程&#xff0c;而数据预处理是数学建模中非常重要的一步。以下是为什么要进行数据预处理的几个原因&#xff1a; 数据质量&#xff1a;原始数据往往存在噪声、异常值、缺失值等问题&#xff0c;这些问题会对…

MATLAB 的 figure 用法总结

文章目录 Syntax&#xff1a;DescriptionExamples1.figure2.figure(Name,Value)Position 属性: 设置 Figure 的位置和大小Name 属性: 设置 Figure 的名称NumberTitle 属性: 取消 Figure 名称里默认的数字units 属性color 属性 3.f figure(___)4.Working with Multiple Figures…