spring-web DispatcherServlet 源码分析

news2024/11/19 1:56:02

说明

  1. 本文基于 jdk 8, spring-framework 5.2.x 编写。
  2. @author JellyfishMIX - github / blog.jellyfishmix.com
  3. LICENSE GPL-2.0

DispatcherServlet 的继承实现层次

关注点应放在 Servlet, GenericServlet, HttpServlet, HttpServletBean, FrameworkServlet, DispatcherServlet

image-20230109110424882

Servlet

javax.servlet.Servlet

package javax.servlet;

public interface Servlet {

    public void init(ServletConfig config) throws ServletException;

    public ServletConfig getServletConfig();

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;

    public String getServletInfo();

    public void destroy();
}

Servlet 接口提供处理请求的能力。请求来自于 web 容器,例如 tomcat, JBoss, Jetty 等。spring-web 应用和 web 容器共同遵守 Servlet 规范。Servlet 位于 javax.servlet 包内,不是 spring 的领域。

GenericServlet

javax.servlet.GenericServlet

package javax.servlet;

public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable {
	public void init(ServletConfig config) throws ServletException {
		this.config = config;
		this.init();
	}
}

GenericServlet 没有实现 service 方法,没有处理请求的逻辑。实现了 init 方法,但是个模板方法,由子类实现,例如 spring 的 HttpServletBean 就实现了该模板方法。GenericServlet 位于 javax.servlet 包内,不是 spring 的领域。

HttpServlet

javax.servlet.http.HttpServlet

  1. 基于 Servlet 规范的应用可以响应所有类型的请求(不仅仅是 HTTP,也可以是 WebSocket, FTP 等),但通常用来处理 HTTP 请求。

  2. Servlet 接口有一个非常重要的实现类 HttpServlet,实现了 service 方法,SpringMVC 框架中的 DispatcherServlet 就是继承自 HttpServlet。HttpServlet 位于 javax.servlet.http 包内,不是 spring 的领域。

package javax.servlet.http;

public abstract class HttpServlet extends GenericServlet {
}

HttpServlet#service 方法

javax.servlet.http.HttpServlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)

  1. 转换为 HttpServletRequest 和 HttpServletResponse。
  2. 判断是何种类型的 http 请求,调用对应的 doXX 方法。GET 请求调用 doGet,POST 请求调用 doPost。doXX 方法是 HttpServlet 接口定义的。
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest  request;
        HttpServletResponse response;

        try {
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
            throw new ServletException("non-HTTP request or response");
        }
        service(request, response);
    }

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }
        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);
        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);
        } else {
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

FrameworkServlet

org.springframework.web.servlet.FrameworkServlet

  1. FrameworkServlet 在 spring 的包中,进入到 spring 的领域。
  2. FrameworkServlet 重写了 doGet(), doPost() 等 doXX 方法,doXX 方法调用了 processRequest() 方法。
package org.springframework.web.servlet;

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
}

FrameworkServlet#processRequest 方法

org.springframework.web.servlet.FrameworkServlet#processRequest

  1. i18n 国际化设置。
  2. 创建 ServletRequestAttributes 对象,初始化上下文 holders,将 request, response 对象放入到线程上下文中。
  3. 调用 doService() 方法,处理请求。核心方法,由子类实现。
	protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;
		// i18n 国际化设置
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		LocaleContext localeContext = buildLocaleContext(request);
		// 创建 ServletRequestAttributes 对象,初始化上下文 holders,将 request, response 对象放入到线程上下文中
		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
		initContextHolders(request, localeContext, requestAttributes);
        
		try {
			// 处理请求,核心方法,由子类实现
			doService(request, response);
		}
		catch (ServletException | IOException ex) {
            // ...
		}
		catch (Throwable ex) {
            // ...
		} finally {
            // ...
		}
	}

FrameworkServlet#initServletBean 方法

org.springframework.web.servlet.FrameworkServlet#initServletBean

  1. FrameworkServlet 还实现了 HttpServletBean 提供的模版方法 initServletBean,初始化 WebApplicationContext。调用 initFrameworkServlet 模板方法,供子类实现初始化工作。
	protected final void initServletBean() throws ServletException {
        // 日志代码移除,保留核心代码
		// 初始化 WebApplicationContext
		this.webApplicationContext = initWebApplicationContext();
        // 模板方法,供子类实现初始化工作
		initFrameworkServlet();
	}

DispatcherServlet#doService 方法

org.springframework.web.servlet.DispatcherServlet#doService

  1. 首先判断是不是 include 请求,如果是则对 request 的 attribute 做个快照备份。
  2. 为 request 设置 WebApplicationContext, 国际化 i18n 相关 resolver, theme 主题相关 resolver
  3. 重定向属性处理,对 redirect 请求的支持。
  4. 调用 DispatcherServlet#doDispatch 方法,核心方法,请求分发给对应的 handler 并处理。
	@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		logRequest(request);

		// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		// 首先判断是不是 include 请求,如果是则对 request 的 attribute 做个快照备份
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// Make framework objects available to handlers and view objects.
		// 为 request 设置 WebApplicationContext
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		// 为 request 设置国际化 i18n 相关 resolver
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		// 为 reqeust 设置 theme 主题相关 resolver
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

		// 重定向属性处理,对 redirect 请求的支持
		if (this.flashMapManager != null) {
			FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
			if (inputFlashMap != null) {
				request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
			}
			request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
			request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
		}

		try {
			// 核心方法,请求分发给对应的 handler 并处理
			doDispatch(request, response);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				// doDispatch 方法执行完后,如果不是异步调用且未完成,对已备份好的快照进行还原,在做完快照后又对 request 设置了一些属性
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

DispatcherServlet#doDispatch

org.springframework.web.servlet.DispatcherServlet#doDispatch

  1. 先检查请求是不是 multipart 请求,如果是则返回已处理的 request,否则不对 request 做额外处理。
  2. 遍历 HandlerMappings 集合,根据 HandlerMapping 获取 HandlerExecutionChain,即 mappedHandler。
  3. 根据 mappedHandler 获取适配的 HandlerAdapter。
  4. 以责任链模式调用 HandlerInterceptor 拦截器的 preHandle 方法(自定义的 spring 拦截器调用位置)。
  5. 调用 HandlerAdapter#handle 方法,处理请求。实际 Controller 最终在这里面被调用,处理完后会返回 ModelAndView。
  6. 将 HttpServletRequest 请求的 uri 转换成 ViewName 字符串并设置到 ModelAndView 中。
  7. 以责任链模式调用 HandlerInterceptor 拦截器的 postHandle 方法(自定义的 spring 拦截器调用位置)。
  8. 渲染 view 视图,调用拦截器的 afterCompletion() 方法。
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				// 先检查请求是不是 multipart 请求,如果是则返回已处理的 request,否则不对 request 做额外处理
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				// 遍历 HandlerMappings 集合,根据 HandlerMapping 获取 HandlerExecutionChain,即 mappedHandler
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				// 根据 mappedHandler 获取适配的 HandlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				// 以责任链模式调用 HandlerInterceptor 拦截器的 preHandle 方法(自定义的 spring 拦截器调用位置)
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				// 调用 HandlerAdapter#handle 方法,处理请求。实际 Controller 最终在这里面被调用,处理完后会返回 ModelAndView。
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				// 将 HttpServletRequest 请求的 uri 转换成 ViewName 字符串并设置到 ModelAndView 中
				applyDefaultViewName(processedRequest, mv);
				// 以责任链模式调用 HandlerInterceptor 拦截器的 postHandle 方法(自定义的 spring 拦截器调用位置)
				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);
			}
			// 渲染 view 视图,调用拦截器的 afterCompletion() 方法
			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 afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

DispatcherServlet#getHandler 方法

org.springframework.web.servlet.DispatcherServlet#getHandler

  1. 遍历 handlerMapping 从中获得 HandlerExecutionChain
  2. DispatcherServlet 初始化时注册的 handlerMapping, @see DispatcherServlet#initStrategies
	@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		// DispatcherServlet 初始化时注册的 handlerMapping, @see DispatcherServlet#initStrategies
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

DispatcherServlet#getHandlerAdapter 方法

org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter

根据 mappedHandler 获取适配的 HandlerAdapter。

  1. 根据 HandlerExecutionChain 获取适配的 HandlerAdapter。责任链模式,遍历 HandlerAdapter 中调用 HandlerAdapter#support() 方法,判断当前 HandlerAdapter 是否支持该处理器,支持就返回该 HandlerAdapter。
  2. DispatcherServlet 初始化时注册的 HandlerAdapter, @see DispatcherServlet#initStrategies
	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		if (this.handlerAdapters != null) {
			for (HandlerAdapter adapter : this.handlerAdapters) {
				if (adapter.supports(handler)) {
					return adapter;
				}
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

DispatcherServlet#applyDefaultViewName 方法

org.springframework.web.servlet.DispatcherServlet#applyDefaultViewName

将 HttpServletRequest 请求的 uri 转换成 ViewName 字符串并设置到 ModelAndView 中

  1. 判断 ModelAndView 是否需要 view 视图层。
  2. getDefaultViewName 方法使用了 RequestToViewNameTranslator,将 HttpServletRequest 请求的 uri 转换成 ViewName 字符串并设置到 ModelAndView 中。
	private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
		// 判断 ModelAndView 是否需要 view 视图层
        if (mv != null && !mv.hasView()) {
            // getDefaultViewName 方法使用了 RequestToViewNameTranslator,将 HttpServletRequest 请求的 uri 转换成 ViewName 字符串并设置到 ModelAndView 中
			String defaultViewName = getDefaultViewName(request);
			if (defaultViewName != null) {
				mv.setViewName(defaultViewName);
			}
		}
	}

	@Nullable
	protected String getDefaultViewName(HttpServletRequest request) throws Exception {
		return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
	}

	@Override
	public String getViewName(HttpServletRequest request) {
		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH);
		return (this.prefix + transformPath(lookupPath) + this.suffix);
	}

DispatcherServlet#processDispatchResult 方法 – 处理 Handler 执行后的结果

org.springframework.web.servlet.DispatcherServlet#processDispatchResult

处理 Handler 执行后的结果,将 ModelAndView 或者 Exception 渲染成视图,然后调用 render() 生成页面。

  1. handler 执行后如果存在 Exception,责任链模式调用 HandlerExceptionResolver,将 Exception 处理成 ModelAndView。
  2. 如果 ModelAndView 不为空,调用 render 方法生成页面。
  3. 以责任链模式调用 HandlerInterceptor#afterCompletion() 方法。
	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		boolean errorView = false;
		// handler 执行后如果存在 Exception,将 Exception 处理为 ModelAndView
		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				// 责任链模式调用 HandlerExceptionResolver,将 Exception 处理成 ModelAndView
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			// 如果 ModelAndView 不为空,调用 render 方法生成页面
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
        // ...

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		// 以责任链模式调用 HandlerInterceptor#afterCompletion() 方法
		if (mappedHandler != null) {
			// Exception (if any) is already handled..
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

DispatcherServlet#render 方法 – 生成 View

org.springframework.web.servlet.DispatcherServlet#render

  1. 责任链模式调用 ViewResolver#resolveViewName 方法,生成 View
	protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// Determine locale for request and apply it to the response.
		Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
		response.setLocale(locale);

		View view;
		String viewName = mv.getViewName();
		if (viewName != null) {
			// We need to resolve the view name.
			// 责任链模式调用 ViewResolver#resolveViewName 方法,生成 View
			view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
			if (view == null) {
				throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
						"' in servlet with name '" + getServletName() + "'");
			}
		} else {
			// No need to lookup: the ModelAndView object contains the actual View object.
			view = mv.getView();
			if (view == null) {
				throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
						"View object in servlet with name '" + getServletName() + "'");
			}
		}

		// Delegate to the View object for rendering.
		if (logger.isTraceEnabled()) {
			logger.trace("Rendering view [" + view + "] ");
		}
		try {
			if (mv.getStatus() != null) {
				response.setStatus(mv.getStatus().value());
			}
			view.render(mv.getModelInternal(), request, response);
		} catch (Exception ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Error rendering view [" + view + "]", ex);
			}
			throw ex;
		}
	}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于Ubuntu20.04文件系统思考

文章目录问题产生Ubuntu文件系统中普通用户可读写地址Ubuntu文件系统Ubuntu文件系统详解一级目录二级目录查找Ubuntu中软件安装位置Ubuntu修改文件权限问题产生 使用electron框架开发桌面端跨平台软件时&#xff0c;当开发完成的程序部署到Ubuntu上&#xff0c;系统无法产生日…

推荐3款远程办公软件

一款好用的远程办公软件能够大大的提高我们的办公效率&#xff0c;在这篇文章中&#xff0c;我们将为您推荐几款常见又好用的远程办公软件&#xff0c;以帮助您能更加高效的远程办公。电脑远程办公软件有很多&#xff0c;本文主要从团队沟通软件、视频会议软件、远程控制软件等…

距离不是拦截我们前进的主因,与社科院杜兰金融硕士一起奔赴山海

最近有咨询社科院杜兰金融管理硕士项目的同学反馈他在西安&#xff0c;读研来北京上课太远了。一直在纠结要不要申请&#xff0c;其实距离不是问题&#xff0c;相向而行才是关键。在项目就读的同学好多也是来自外地&#xff0c;他们克服了种种困难来到项目学习&#xff0c;就是…

kubernetes教程 --service详解

Service 介绍 在kubernetes中&#xff0c;pod是应用程序的载体&#xff0c;我们可以通过pod的ip来访问应用程序&#xff0c;但是pod的ip地址不是固定的&#xff0c;这也就意味着不方便直接采用pod的ip对服务进行访问。 为了解决这个问题&#xff0c;kubernetes提供了Service…

行业分析| 智能无人自助设备

智能无人自助设备运用二维码技术、音视频通信技术和AI智能技术等相结合&#xff0c;提供了无人超市、自动售货机、智能快递柜等。当下很多商业地区或社区&#xff0c;都放置了智能无人自助设备&#xff0c;不仅可以为商家节省时间和精力、提升运营环境&#xff0c;也可以为众多…

宽刈幅干涉雷达高度计SWOT(Surface Water and Ocean Topography)卫星进展(待完善)

> 以下信息搬运自SWOT官方网站等部分文献资料&#xff0c;如有侵权请联系&#xff1a;sunmingzhismz163.com > 排版、参考文献、部分章节待完善 > 2023.02.17.22:00 初稿概况 2022年12月16日地表水与海洋地形卫星SWOT (Surface Water and Ocean Topography)在加利福尼…

SpringCloud(一)注册中心

注册中心eureka服务端客户端负载均衡nacos服务端客户端nacos分级存储模型配置集群属性nacos环境隔离-namespace临时实例和非临时实例Eureka和Nacos的异同负载均衡策略饥饿加载eureka 服务端 依赖 <!-- eureka注册中心服务端依赖--><dependencies><depe…

【数据结构与算法】1.树、二叉树、字典树、红黑树

文章目录简介1.树 (Tree)2.二叉树&#xff08;Binary Tree&#xff09;2.1.二叉树数据结构2.2.二叉树的三种遍历方式3.二叉查找树(Binary Search Tree)3.1.二叉查找树的概念和定义3.2.二分查找算法4.字典树(Trie)5.红黑树(Red-Black Tree)简介 本章主要讲解一些树的基本概念,二…

windows10/11,傻瓜式安装pytorch(gpu),在虚拟环境anaconda

安装anaconda地址 &#xff1a;Anaconda | The Worlds Most Popular Data Science Platform安装选项全默认点击next就行。查看支持cuda版本cmd命令行输入nvidia-smi。下图右上角显示11.6为支持的cuda版本。要是显示没有nvidia-smi命令。得安装nvidia驱动&#xff0c;一般情况都…

字符串匹配 - Overview

字符串匹配(String Matchiing)也称字符串搜索(String Searching)是字符串算法中重要的一种&#xff0c;是指从一个大字符串或文本中找到模式串出现的位置。字符串匹配概念字符串匹配问题的形式定义&#xff1a;文本&#xff08;Text&#xff09;是一个长度为 n 的数组 T[1..n]&…

Nodejs的安装

1. Nodejs的真正用途 a. 一个javascirpt的运行环境 b. 运行在服务器&#xff0c;作为web server c. 运行在本地&#xff0c;作为打包&#xff0c;构建工具 2. Nodejs的下载和安装 a. 普通方式&#xff08;访问官网&#xff09; 下载对应系统版本即可&#xff08;个人学习可无需…

【数据结构与算法分析】介绍蛮力法以及相关程序案例

文章目录蛮力法之排序选择排序冒泡排序实际应用蛮力法之最近对和凸包问题最近对问题凸包问题蛮力法(brute force)&#xff0c;其本质跟咱常说的暴力法是一样的&#xff0c;都是一种简单直接地解决问题的方法&#xff0c;通常直接基于问题的描述和所涉及的概念定义进行求解。 蛮…

【嵌入式】HC32F460串口接收超时中断+DMA

一 项目背景 项目需要使用一款UART串口编码器&#xff0c;编码器的数据以波特率57600持续向外发送。但这组数据包没有固定的包头和校验尾&#xff0c;仅仅是由多圈圈数和单圈角度组成的六字节数据码&#xff0c;这样接收到的数组无法确定实际的下标&#xff0c;所以这边考虑用串…

8月起,《PMBOK®指南(第七版)》将被采用,考PMP的注意了!

PMP第七版教材采用时间定了&#xff01;&#xff01;&#xff01;2023年【8月开始】第一次使用第七版教材&#xff0c;通知明显指出&#xff0c;第六版的关键知识任然还是有效的。第七版做的调整还是蛮大的&#xff0c;首次提出了项目管理的 12 项原则和8个项目绩效域&#xff…

Java基础语法小结来啦

简单的来说&#xff0c;一个java的程序他是有一系列对象的集合组成&#xff0c;通过对这些对象相互间调用的方式协同工作&#xff0c;下面就是我有关于Java基础语法的一些小结。 一、return简单使用 下面来一个Java程序&#xff0c;表示的是在self1这个包中我们创建了一个名叫…

Skywalking ui页面功能介绍

菜单栏 仪表盘&#xff1a;查看被监控服务的运行状态&#xff1b; 拓扑图&#xff1a;以拓扑图的方式展现服务之间的关系&#xff0c;并以此为入口查看相关信息&#xff1b; 追踪&#xff1a;以接口列表的方式展现&#xff0c;追踪接口内部调用过程&#xff1b; 性能剖析&am…

GEE学习笔记 八十:批量下载影像

最近问如何批量导出集合的小伙伴非常多&#xff0c;一个一个回复太麻烦&#xff0c;我这里直接给一段例子代码吧&#xff1a; var l8 ee.ImageCollection("LANDSAT/LC08/C01/T1_SR"); var roi /* color: #d63000 */ee.Geometry.Polygon( [[[115.64960937…

从0到1一步一步玩转openEuler--17 openEuler DNF(YUM)检查更新

文章目录17.1 检查更新17.2 升级17.3 更新所有的包和它们的依赖DNF是一款Linux软件包管理工具&#xff0c;用于管理RPM软件包。DNF可以查询软件包信息&#xff0c;从指定软件库获取软件包&#xff0c;自动处理依赖关系以安装或卸载软件包&#xff0c;以及更新系统到最新可用版本…

Nacos框架服务注册发现和配置中心原理

文章目录1.简介2.整体架构和原理2.1 服务发现注册原理2.1.1 注册和拉取数据2.1.2 Server集群一致性2.1.3 健康检查2.2 配置中心原理2.2.1 支持功能和资源模型2.2.2 server集群数据一致性问题2.2.3 client和server的通信监听改动方式2.2.4 client拉取数据2.2.5 client请求server…

kubernetes教程 --Pod生命周期

Pod生命周期 pod创建过程运行初始化容器&#xff08;init container&#xff09;过程运行主容器&#xff08;main container&#xff09;过程 容器启动后钩子&#xff08;post start&#xff09;、容器终止前钩子&#xff08;pre stop&#xff09;容器的存活性探测&#xff08;…