SpringMVC 源码分析之 DispatcherServlet

news2024/11/26 12:26:30

SpringMVC 源码分析之 DispatcherServlet

  • FrameworkServlet
    • service
    • processRequest
      • LocaleContext 和 RequestAttributes
        • LocaleContext
        • RequestAttributes
      • 事件发布
  • DispatcherServlet
    • doService
      • 代码分析
    • doDispatch
      • 参数含义
      • 具体的处理逻辑:
    • processDispatchResult
  • 引用

SpringMVC 的核心是 DispatcherServlet,和所有的 Servlet 一样,DispatcherServlet 对请求的处理也是从 service 方法开始,而 DispatcherServlet 的 service 方法在父类 FrameworkServlet 中,因此我们先来看看 FrameworkServlet,这有助于我们理解 DispatcherServlet。

FrameworkServlet

FrameworkServlet 继承自 HttpServletBean,而 HttpServletBean 继承自 HttpServlet,HttpServlet 就是 JavaEE 里边的东西了,这里我们不做讨论,从 HttpServletBean 开始就是框架的东西了,但是 HttpServletBean 比较特殊,它的特殊在于它没有进行任何的请求处理,只是参与了一些初始化的操作,这些比较简单,所以这里我们对 HttpServletBean 不做分析,就直接从它的子类 FrameworkServlet 开始看起。

service

FrameworkServlet#service 源码如下:

	/**
	 * Override the parent class implementation in order to intercept PATCH requests.
	 */
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
		if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
			processRequest(request, response);
		}
		else {
			super.service(request, response);
		}
	}

可以看到,在该方法中,首先获取到当前请求方法,然后对 patch 请求额外关照了下,其他类型的请求统统都是 super.service 进行处理。

然而在 HttpServlet 中并未对 doGet、doPost 等请求进行实质性处理,所以 FrameworkServlet 中还重写了各种请求对应的方法,如 doDelete、doGet、doOptions、doPost、doPut、doTrace 等,其实就是除了 doHead 之外的其他方法都重写了。

doDelete、doGet、doPost 以及 doPut 四个方法:

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
 processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
 processRequest(request, response);
}
@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
 processRequest(request, response);
}
@Override
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
 processRequest(request, response);
}

可以看到,这里又把请求交给 processRequest 去处理了,在 processRequest 方法中则会进一步调用到 doService,对不同类型的请求分类处理。

doOptions 和 doTrace 则稍微有些差异,如下:

@Override
protected void doOptions(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
 if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {
  processRequest(request, response);
  if (response.containsHeader("Allow")) {
   return;
  }
 }
 super.doOptions(request, new HttpServletResponseWrapper(response) {
  @Override
  public void setHeader(String name, String value) {
   if ("Allow".equals(name)) {
    value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name();
   }
   super.setHeader(name, value);
  }
 });
}
@Override
protected void doTrace(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
 if (this.dispatchTraceRequest) {
  processRequest(request, response);
  if ("message/http".equals(response.getContentType())) {
   return;
  }
 }
 super.doTrace(request, response);
}

可以看到这两个方法的处理多了一层逻辑,就是去选择是在当前方法中处理对应的请求还是交给父类去处理,由于 dispatchOptionsRequest 和 dispatchTraceRequest 变量默认都是 false,因此默认情况下,这两种类型的请求都是交给了父类去处理。

processRequest

	protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;

		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		LocaleContext localeContext = buildLocaleContext(request);

		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) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			logResult(request, response, failureCause, asyncManager);
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

这个方法虽然比较长,但是其实它的核心就是最中间的 doService 方法,以 doService 为界,我们可以将该方法的内容分为三部分:

  1. doService 之前主要是一些准备工作,准备工作主要干了两件事,第一件事就是从 LocaleContextHolder 和 RequestContextHolder 中分别获取它们原来保存的 LocaleContext 和 RequestAttributes 对象存起来,然后分别调用 buildLocaleContext 和 buildRequestAttributes 方法获取到当前请求的 LocaleContext 和 RequestAttributes 对象,再通过 initContextHolders 方法将当前请求的 LocaleContext 和 RequestAttributes 对象分别设置到 LocaleContextHolder 和 RequestContextHolder 对象中;第二件事则是获取到异步管理器并设置拦截器。
  2. 接下来就是 doService 方法,这是一个抽象方法,具体的实现在 DispatcherServlet 中,这个后边放到 DispatcherServlet 中再分析。
  3. 第三部分就是 finally 中,这个里边干了两件事:第一件事就是将 LocaleContextHolder 和 RequestContextHolder 中对应的对象恢复成原来的样子(参考第一步);第二件事就是通过 publishRequestHandledEvent 方法发布一个 ServletRequestHandledEvent 类型的消息。

processRequest 其实主要做了两件事,第一件事就是对 LocaleContext 和 RequestAttributes 的处理,第二件事就是发布事件。我们对这两件事分别来研究。

LocaleContext 和 RequestAttributes

LocaleContext 和 RequestAttributes 都是接口,不同的是里边存放的对象不同。

LocaleContext

LocaleContext 里边存放着 Locale,也就是本地化信息,如果我们需要支持国际化,就会用到 Locale。 SpringMVC 中还给我们提供了 LocaleContextHolder,这个工具就是用来保存当前请求的 LocaleContext 的。

RequestAttributes

RequestAttributes 是一个接口,这个接口可以用来 get/set/remove 某一个属性。
RequestAttributes 有诸多实现类,默认使用的是 ServletRequestAttributes,通过 ServletRequestAttributes,我们可以 getRequest、getResponse 以及 getSession。和 LocaleContext 类似,RequestAttributes 被保存在 RequestContextHolder 中,RequestContextHolder 的原理也和 SecurityContextHolder 类似。

事件发布

processRequest 方法中的事件发布,在 finally 代码块中会调用 publishRequestHandledEvent 方法发送一个 ServletRequestHandledEvent 类型的事件。正常情况下,这个事件总是会被发送出去,如果项目有需要,我们可以监听该事件【如此监听每一个请求处理时间等】。

DispatcherServlet

DispatcherServlet 是 SpringMVC 的大脑,它负责整个 SpringMVC 的调度工作,是 SpringMVC 中最最核心的类,SpringMVC 整个顶层架构设计都体现在这里,所以搞明白 DispatcherServlet 的源码,基本上 SpringMVC 的工作原理也就了然于胸了。

经过上边的分析,可以看到 DispatcherServlet 的入口方法是 doService,接下来就从 doService 方法开始看起,一步一步揭开 DispatcherServlet 的面纱。

doService

先来看 doService,源码如下:

	@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.
		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.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

		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 {
			doDispatch(request, response);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

代码分析

  1. 打印日志【debug 及 trace 级别(日志信息包含 是否是转发,接口路径,请求方式,请求头,URL路径参数及请求体参数)】
  2. 首先判断当前请求是不是 include 请求,如果是 include,则对 request 的 attribute 做一个快照备份,在最后的 finally 中再对备份的属性进行还原。
  3. 接下来对 request 设置一些常见属性,例如应用上下文、国际化的解析器、主题解析器等等,这些东西在初始化的时候已经准备好了,这里只是应用。
  4. 接下来处理 flashMap,如果存在 flashMap 则进行复原。
  5. 接下来处理 RequestPath,将请求路径对象化以备后续使用(在后面的请求映射匹配时会用到)。
  6. 调用 doDispatch 方法进行下一步处理。
  7. 还原快照属性、还原 RequestPath。

核心在于 doDispatch 方法,所以接下来我们就来看看 doDispatch 方法。

doDispatch

doDispatch 方法所做的事情就比较多了,代码如下:

	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 {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				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;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

				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 afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

参数含义

doDispatch 方法其实主要做了两方面的事情:请求处理以及页面渲染,其中各个初始变量的含义如下:

  1. processedRequest:这个用来保存实际上所用的 request 对象,在后面的流程中【checkMultipart(request)】会对当前 request 对象进行检查,如果是文件上传请求,则会对请求重新进行封装,如果不是文件上传请求,则继续使用原来的请求。
  2. multipartRequestParsed:表示是否是文件上传请求的标记【上步处理结束,上传文件的请求会重新封装请求对象,故这里是通过检验 request 对象是否还是同一个对象,来标识是否是文件上传请求】。
  3. mappedHandler:这是具体处理请求的处理器链,处理器链包含两方面的东西:请求处理器和对应的 Interceptor。
  4. asyncManager:这是一个异步请求管理器。
  5. mv:这是最终渲染返回的 ModelAndView 对象。
  6. dispatchException:表示请求处理过程中所抛出的异常,这个异常不包括渲染过程抛出的异常!

具体的处理逻辑:

  1. 首先通过 checkMultipart 检查是不是文件上传请求,如果是,则对当前 request 重新进行包装,如果不是,则直接将参数返回。
  2. 如果 processedRequest 不等于 request,则说明当前请求是文件上传请求(request 在 checkMultipart 方法中被重新封装了),否则说明当前请求不是文件上传请求。
  3. 根据当前请求,调用 getHandler 方法获取请求处理器,如果没找到对应的请求处理器,则调用 noHandlerFound 方法抛出异常或者给出 404。
  4. 接下来再调用 getHandlerAdapter 方法,根据当前的处理器找到处理器适配器。
  5. 然后处理 GET 和 HEAD 请求头的 Last_Modified 字段。当浏览器第一次发起 GET 或者 HEAD 请求时,请求的响应头中包含一个 Last-Modified 字段,这个字段表示该资源最后一次修改时间,以后浏览器再次发送 GET、HEAD 请求时,都会携带上该字段,服务端收到该字段之后,和资源的最后一次修改时间进行对比,如果资源还没有过期,则直接返回 304 告诉浏览器之前的资源还是可以继续用的,如果资源已经过期,则服务端会返回新的资源以及新的 Last-Modified。
  6. 接下来调用拦截器的 preHandle 方法,如果该方法返回 false,则直接 return 掉当前请求
  7. 接下来执行 ha.handle 去调用真正的请求,获取到返回结果 mv。
  8. 接下来判断当前请求是否需要异步处理,如果需要,则直接 return 掉。
  9. 如果不需要异步处理,则执行 applyDefaultViewName 方法,检查当前 mv 是否没有视图,如果没有(例如方法返回值为 void),则给一个默认的视图名。
  10. 接下来调用 applyPostHandle 方法执行拦截器里边的 postHandle 方法。
  11. 接下来调用 processDispatchResult 方法对执行结果进行处理,包括异常处理、渲染页面以及执行拦截器的 afterCompletion 方法都在这里完成。
  12. 最后在 finally 代码块中判断是否开启了异步处理,如果开启了,则调用相应的拦截器;如果请求是文件上传请求,则再调用 cleanupMultipart 方法清除文件上传过程产生的一些临时文件。

这是 doDispatch 方法的一个大致执行逻辑,doDispatch 里边的 try-catch 有两层,最里边那一层,抛出来的异常会被赋值给 dispatchException 变量,这些异常最终在 processDispatchResult 方法中被处理掉,外面的异常则是 processDispatchResult 方法在执行的过程中抛出的异常,一般来说主要是页面渲染时候的异常。

processDispatchResult

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) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace("No view rendering, null ModelAndView returned.");
			}
		}

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

		if (mappedHandler != null) {
			// Exception (if any) is already handled..
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

在 processDispatchResult 方法中首先对异常进行了处理,配置好异常对应的 ModelAndView,然后调用 render 方法对页面进行渲染,最后通过 triggerAfterCompletion 方法去触发拦截器的 afterCompletion 方法。

在这里插入图片描述

引用

  1. SpringMVC 源码分析之 DispatcherServlet
  2. SpringMVC 源码分析之 FrameworkServlet

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

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

相关文章

CH2023、Adobe Character Animator 2023(动画角色制作软件)下载教程、安装教程

最后附下载地址 Adobe CH简介&#xff1a; Adobe Character Animator是一款基于动画制作的软件&#xff0c;它可以将手绘的角色通过摄像头或麦克风捕捉到的实时动作转化为动画效果。该软件结合了人工智能和动画技术&#xff0c;可以快速创建高质量的角色动画&#xff0c;并且…

构建基于前后端分离的医学影像学学习平台:Java技术实现与深度解析

在医学领域,影像学学习平台是一种重要的工具,用于帮助医学学生和专业人士学习和研究医学影像。本文将介绍如何使用Java构建一个基于前后端分离的医学影像学学习平台,通过结合前沿的Web开发技术和医学影像处理算法,为用户提供强大且高效的学习工具。 技术架构设计: 在构…

华为OD机试之阿里巴巴找黄金宝箱(II)(Java源码)

阿里巴巴找黄金宝箱(II) 题目描述 一贫如洗的樵夫阿里巴巴在去砍柴的路上&#xff0c;无意中发现了强盗集团的藏宝地&#xff0c;藏宝地有编号从0~n 的箱子&#xff0c;每个箱子上面贴有箱子中藏有金币的数量。 从金币数量中选出一个数字集合&#xff0c;并销毁贴有这些数字的…

Visual ChatGPT原理解读——大模型论文阅读笔记四

论文&#xff1a;https://arxiv.org/abs/2303.04671 代码&#xff1a;https://github.com/microsoft/TaskMatrix 一. 整体框架 如图所示&#xff0c;用户上传一张黄花的图像并输入一个复杂的语言指令“请根据该图像的预测深度生成一朵红花&#xff0c;然后逐步使其像卡通一样”…

go-zero学习 第一章 基础

go-zero学习 第一章 基础 重要提示1 相关命令2 参考文档3 架构图4 go-zero环境搭建4.1 注意事项4.2 go-zero 需要安装的组件4.3 自动安装4.4 手动安装 5 单体服务5.1 简单入门5.2 api语法5.3 api 文件格式化 6 微服务6.1 简单入门6.2 proto 语法 7 目录结构介绍 重要提示 因官…

spring boot绮梦餐饮系统-计算机毕设 附源码88672

spring boot绮梦餐饮系统 目录 毕业设计 摘 要 Abstract 第1章 前 言 1.1 研究背景 1.2 研究现状 1.3 系统开发目标 第2章 系统开发环境 2.1 J2EE技术 2.2 Spring boot框架 2.3 MySQL数据库 2.4 B/S结构 2.5 JavaScript 运行模式 1.1是一种解释性脚本语言&…

Segment Anything Model(SAM)如何促进医学图像分割

文章目录 How Segment Anything Model (SAM) Boost Medical Image Segmentation?摘要segment anything modelHow SAM performs on Medical Image Segmentation?Pathology Image SegmentationLiver Tumor Segmentation from CECTPolyps Segmentation from Colonoscopy ImagesB…

Win10 hyper-v与vmware不兼容解决方案

Win10 hyper-v与vmware不兼容怎么办 一、异常1.1 异常描述 - V M w a r e W o r k s t a t i o n 与 H y p e r − V 不兼容 \color{red}{VMware Workstation 与 Hyper-V 不兼容} VMwareWorkstation与Hyper−V不兼容1.2 异常原因 二、解决办法2.1 关闭Hyper-V启动2.2 关闭内核…

【面试题】6月 vue核心面试题汇总

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 一、MVVM原理 在Vue2官方文档中没有找到Vue是MVVM的直接证据&#xff0c;但文档有提到&am…

判断数组中的每个元素是否为NaN numpy.isnan()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 判断数组中的每个元素是否为NaN numpy.isnan() [太阳]选择题 请问关于以下代码的最后输出正确的是&#xff1f; import numpy as np a np.array([1, np.nan, np.inf, float(nan)]) print(&…

PSD矢量处理:Graphics Mill 11.1.18 Crack

光栅和矢量处理 .NET SDK 图形铣床 11 图PSD矢量处理:Graphics Mill 11.1.18可供 .NET 和 ASP.NET 开发人员使用的最可靠的映像工具集。它允许用户轻松地向 .NET 应用添加复杂的光栅和矢量图像处理功能。 光栅图形 加载并保存 JPEG、PNG 和另外 8 种图像格式 调整大小&#xff…

SignalR服务主动发送消息给客户端

文章目录 背景尝试方案使用IHubContext上下文进行处理第一步 创建一个类用于处理上下文&#xff08;WarningBroadcast&#xff09;第二步&#xff1a;如何实例化这个对象呢下面是我实现的方式 第三步&#xff1a;使用扩展类 调用通知关于MessageBus的使用demo发布消息订阅消息 …

java 心理教育网站系统Myeclipse开发mysql数据库web结构jsp编程计算机网页项目

一、源码特点 java 心理教育网站系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

使用 Flask 的g对象和 MySQL 实现用户登录和注销功能

在Flask中&#xff0c;g对象是一个特殊类型的对象&#xff0c;被称为"上下文全局变量"。它在一次请求的生命周期内存储信息&#xff0c;并且可以在整个请求过程中访问和共享。 g对象对于存储用户登录信息非常有用。例如&#xff0c;当用户通过验证登录时&#xff0c…

编译程序的任务

编译程序是一种翻译程序&#xff0c;编译程序是将一种语言形式翻译成另一种语言形式。它将高级语言所写的源程序翻译成等价的机器语言或汇编语言的目标程序。 整个编译过程一般可以划分为 5 个阶段&#xff1a;词法分析、语法分析、语义分析及中间代码生成、中间代码优化和目标…

UOS系统VMware安装教程

官网链接 https://home.uniontech.com/ 从这里下镜像文件 内存最好4以上 选择ISO文件地址 选择自定义安装 手动分区 4个g给交换分区&#xff0c;剩下的全挂在根目录下

长视频自动化摘要笔记完整工作流;腾讯云发布AIGC全链路内容安全解决方案

&#x1f989; AI新闻 &#x1f680; 腾讯云发布AIGC全链路内容安全解决方案&#xff0c;助力企业护航生成式人工智能健康发展 摘要&#xff1a;腾讯云公布MaaS能力全景图&#xff0c;提供AIGC全链路内容安全解决方案&#xff0c;覆盖从模型训练到内容生成再到事后运营全过程…

【工程应用八】终极的基于形状匹配方案解决(小模型+预生成模型+无效边缘去除+多尺度+各项异性+最小组件尺寸)

我估摸着这个应该是关于形状匹配或者模版匹配的最后一篇文章了&#xff0c;其实大概是2个多月前这些东西都已经弄完了&#xff0c;只是一直静不下来心整理文章&#xff0c;提醒一点&#xff0c;这篇文章后续可能会有多次修改&#xff08;但不会重新发文章&#xff0c;而是在后台…

搭建GPU环境的TensorFlow并将单块GPU划分逻辑为分区的实践

1、搭建环境 1.1、查看GPU版本 在安装tensorflow的GPU版本之前可以先看下自己的显卡情况 命令&#xff1a;nvidia-smi 或者桌面右下角&#xff0c;NVIDIA图标&#xff0c;进入到NVIDIA的控制面板&#xff1a; 点击左下角的系统信息&#xff0c;组件中查看NVCUDA64.dll的版本…

英特尔淡化混合 CPU-GPU 引擎,将 NNP 合并到 GPU 中

早在 2022 年 2 月&#xff0c;当英特尔宣布其“Falcon Shores”项目以构建混合 CPU-GPU 计算引擎时&#xff0c;该项目允许在单个插槽中独立扩展 CPU 和 GPU 容量&#xff0c;看起来这家芯片制造商正准备与竞争对手 Nvidia 较量AMD 开始使用混合计算电机&#xff0c;Intel 称之…