当前位置: 首页 > news >正文

Spring MVC 五 - Spring MVC的配置和DispatcherServlet初始化过程

今天的内容是SpringMVC的初始化过程,其实也就是DispatcherServilet的初始化过程。

Special Bean Types

DispatcherServlet委托如下一些特殊的bean来处理请求、并渲染正确的返回。这些特殊的bean是Spring MVC框架管理的bean、按照Spring框架的约定处理相关请求,一般情况下是框架内置的,我们当然也可以定制或扩展他们的功能。

这些特殊bean包括:

  1. HandlerMapping:根据一定的规则把请求映射到对应的HandlerMapping去处理,HandlerMapping可以包含一系列拦截器,进行前置或后置处理。框架默认提供了RequestMappingHandlerMapping(处理@RequestMapping注解方法的)和SimpleUrlHandlerMapping两个HandlerMapping。
  2. HandlerAdapter:HandlerMapping匹配到请求之后,调用HandlerAdapter具体处理请求。
  3. HandlerExceptionResolver:发生异常后的异常处理器。
  4. ViewResolver:处理返回
  5. LocaleResolver, LocaleContextResolver:本地化处理器
  6. ThemeResolver:Theme渲染处理器
  7. MultipartResolver:Multipart处理器,文件上传下载的处理。
  8. FlashMapManager:跨请求存储和获取“input”和“output”的处理器

Web MVC Config

DispatcherServlet初始化过程中会根据WebApplicationContext的配置(xml或注解方式,前面两篇文章分析过)完成上述特殊bean的初始化,如果DispatcherServlet在WebApplicationContext中没有发现相应的配置,则采用DispatcherServlet.properties文件中的默认配置完成初始化。

DispatcherServlet.properties文件在Spring web mvc包下:
在这里插入图片描述

我们猜想Spring MVC框架是通过DispatcherServlet的init方法完成上述各特殊bean的初始化的,下面我们要详细分析一下具体的初始化过程。

Servlet Config

通过注解方式、或通过xml方式初始化DispatcherServlet的具体方法,前面两篇文章已经做过分析,此处不在赘述。

DispatcherServlet的初始化

众所周知,Servlet容器(比如Tomcat)会通过调用Servlet的init方法完成Servlet的初始化。

我们接下来看一下DispatcherServlet的初始化过程,也就是DispatcherServlet的init方法。

先来看一眼DispatcherServlet的类结构:
在这里插入图片描述

init方法在他的父类HttpServletBean中:

	@Overridepublic final void init() throws ServletException {// Set bean properties from init parameters.PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);if (!pvs.isEmpty()) {try {BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));initBeanWrapper(bw);bw.setPropertyValues(pvs, true);}catch (BeansException ex) {if (logger.isErrorEnabled()) {logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);}throw ex;}}// Let subclasses do whatever initialization they like.initServletBean();}

上面的代码是对当前Servlet属性的处理,与我们的目标无关,初始化逻辑在最下面的方法initServletBean中,在他的子类(也是DispatcherServlet的直接父类)FrameworkServlet中:

	protected final void initServletBean() throws ServletException {...省略部分代码try {this.webApplicationContext = initWebApplicationContext();initFrameworkServlet();}catch (ServletException | RuntimeException ex) {logger.error("Context initialization failed", ex);throw ex;}

该方法中有很多打印log的代码,忽略掉,剩下的就是两个方法的调用:一个是创建webApplicationContext的,一个是initFrameworkServlet,这个initFrameworkServlet是空方法,所以,DispatcherServlet的初始化逻辑,关键就在这个initWebApplicationContext()方法中。

initWebApplicationContext方法很长,我们分段分析一下。

	protected WebApplicationContext initWebApplicationContext() {WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;...

首先获取当前ServletContext的RootContext,有关RootContext,参见前面的文章 Spring MVC 四:Context层级。

然后:

		if (this.webApplicationContext != null) {// A context instance was injected at construction time -> use itwac = this.webApplicationContext;if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;if (!cwac.isActive()) {// The context has not yet been refreshed -> provide services such as// setting the parent context, setting the application context id, etcif (cwac.getParent() == null) {// The context instance was injected without an explicit parent -> set// the root application context (if any; may be null) as the parentcwac.setParent(rootContext);}configureAndRefreshWebApplicationContext(cwac);}}}

判断如果DispatcherServlet对象创建的时候,如果在构造方法中已经初始化过WebApplicationContext了,那么就使用该WebApplicationContext,设置上面获取到的RootContext为当前WebApplicationContext的父容器。并且判断该Context是否已经刷新过,如果没有刷新过的话,调用configureAndRefreshWebApplicationContext方法配置并刷新该Context。

前面文章Spring MVC 三 :基于注解配置中我们分析过DispatcherServlet的创建过程,确实在创建的时候就通过构造函数的参数传过来已经创建好的ServletContext了:

protected void registerDispatcherServlet(ServletContext servletContext) {String servletName = getServletName();Assert.hasLength(servletName, "getServletName() must not return null or empty");WebApplicationContext servletAppContext = createServletApplicationContext();Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());...省略代码

所以如果是通过注解方式配置的话,会通过createServletApplicationContext()方法创建ServletContext:

	@Overrideprotected WebApplicationContext createServletApplicationContext() {AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();Class<?>[] configClasses = getServletConfigClasses();if (!ObjectUtils.isEmpty(configClasses)) {context.register(configClasses);}return context;}

最终创建的ServletContext是AnnotationConfigWebApplicationContext。

所以如果通过注解方式配置,那就是要走到上面这段逻辑中来的。

否则,如果不是通过注解、而是通过xml配置,也就是说DispactherServlet创建的时候并没有ServletContext,会走到下面的逻辑中:

		if (wac == null) {// No context instance was injected at construction time -> see if one// has been registered in the servlet context. If one exists, it is assumed// that the parent context (if any) has already been set and that the// user has performed any initialization such as setting the context idwac = findWebApplicationContext();}if (wac == null) {// No context instance is defined for this servlet -> create a local onewac = createWebApplicationContext(rootContext);}

如果wac为空(DispatcherServlet创建的时候没有设置),那么就判断容器中是否已经注册进来了,如果已经注册了的话,那么Spring framework就会认为其父容器已经设置过了,也做过初始化以及refresh了,直接拿过来用就OK。(我们的应用如果不主动注册的话,就不会有注册进来的Context,所以这段代码就跑不到)。

然后看下面的代码,如果没有发现,就调用createWebApplicationContext创建,createWebApplicationContext方法在创建WebApplicationContext之后,也会设置其父容器为RootContext,之后也会调用configureAndRefreshWebApplicationContext配置和刷新容器,走到和上面第一步(通过注解方式配置,DispatcherServlet创建的时候已经通过构造器设置了一个Context)一致的逻辑中了。

createWebApplicationContext:

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {Class<?> contextClass = getContextClass();if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException("Fatal initialization error in servlet with name '" + getServletName() +"': custom WebApplicationContext class [" + contextClass.getName() +"] is not of type ConfigurableWebApplicationContext");}ConfigurableWebApplicationContext wac =(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);wac.setEnvironment(getEnvironment());wac.setParent(parent);String configLocation = getContextConfigLocation();if (configLocation != null) {wac.setConfigLocation(configLocation);}configureAndRefreshWebApplicationContext(wac);return wac;}

首先调用getContextClass()方法获取contextClass:

	public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;public Class<?> getContextClass() {return this.contextClass;}

可以看到,如果不是通过注解方式启动、而是通过xml配置方式启动的话,创建的ServletContext应该就是这个XmlWebApplicationContext。

创建ServletContext之后,与xml配置方式一样:设置父容器,然后调用configureAndRefreshWebApplicationContext方法配置及刷新容器。

接下来我们看configureAndRefreshWebApplicationContext方法。

configureAndRefreshWebApplicationContext

目前为止,我们前面的猜测:通过DispatcherServlet的init方法初始化各个特殊bean。尚未的到证实 — 在DispatcherServlet的init方法中,我们尚未看到相关的初始化代码。

不过代码还没分析完,还有一个configureAndRefreshWebApplicationContext,我们继续分析。

代码比较长,我们还是分段分析:

	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {if (ObjectUtils.identityToString(wac).equals(wac.getId())) {// The application context id is still set to its original default value// -> assign a more useful id based on available informationif (this.contextId != null) {wac.setId(this.contextId);}else {// Generate default id...wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());}}

为WebApplicationContext设置Id,无关紧要,继续看下面的代码:

		wac.setServletContext(getServletContext());wac.setServletConfig(getServletConfig());wac.setNamespace(getNamespace());wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

设置ServletContext、ServletConfig、以及namespace,之后新增了一个监听器:ContextRefreshListener()。

然后:

		// The wac environment's #initPropertySources will be called in any case when the context// is refreshed; do it eagerly here to ensure servlet property sources are in place for// use in any post-processing or initialization that occurs below prior to #refreshConfigurableEnvironment env = wac.getEnvironment();if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());}postProcessWebApplicationContext(wac);applyInitializers(wac);wac.refresh();}

设置环境变量,以及获取初始化参数,最后调用WebApplicationContext的refresh方法。

依然没有看到DispatcherServlet对特殊bean的初始化!而且现在的代码逻辑是转到了ApplicationContext中,是Spring Framework的内容、并不是Spring MVC的内容。

别急,马上就要摸到开关了!

目前的代码确实是转悠到Spring Framework中来了。所以说Spring全家桶,不管是Spring MVC、还是SpringBoot、还是Spring Security,统统都是以Spring Framework为基础的。掌握Spring Framework是掌握Spring全家桶的基础。

ApplicationContext的refresh方法我们很熟悉了,是Spring Framework的关键方法,在AbstractApplicationContext类中实现,该方法最后会调用到finishRefresh()方法:

在这里插入图片描述

finishRefresh()方法最后会发布ContextRefreshedEvent事件。

没错,前面代码分析过程中,我们确实是在WebApplicationContext容器中注册了一个针对该事件的监听器ContextRefreshListener:

	private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {FrameworkServlet.this.onApplicationEvent(event);}}

该监听器是定义在FrameworkServlet中的一个内部类,其onApplicationEvent方法会调用到FrameworkServlet的onApplicationEvent方法,这样,通过监听机制,代码逻辑就再次转回到了DispatcherServlet(确切说是他的父类FrameworkServlet)中来了:

	public void onApplicationEvent(ContextRefreshedEvent event) {this.refreshEventReceived = true;synchronized (this.onRefreshMonitor) {onRefresh(event.getApplicationContext());}}

最终会调用到DispatcherServlet中来:

	@Overrideprotected void onRefresh(ApplicationContext context) {initStrategies(context);}

查看DispatcherServlet代码我们会发现,这个initStrategies正式我们要找的方法,方法参数Context是通过事件传递过来的,因此,DispatcherSerlet在进行初始化的时候可以持有ApplicationContext对象,然后,随心所欲地完成Spring MVC特殊bean的初始化。

篇幅原因,关于DispatcherServlet的具体初始化过程,我们后面分析。

上一篇 Spring MVC 四:Context层级

相关文章:

Spring MVC 五 - Spring MVC的配置和DispatcherServlet初始化过程

今天的内容是SpringMVC的初始化过程&#xff0c;其实也就是DispatcherServilet的初始化过程。 Special Bean Types DispatcherServlet委托如下一些特殊的bean来处理请求、并渲染正确的返回。这些特殊的bean是Spring MVC框架管理的bean、按照Spring框架的约定处理相关请求&…...

Ramp 有点意思的题目

粗一看都不知道这个要干什么&#xff0c;这 B 装得不错。 IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwoKJycnCktlZXAgdXMgb3V0IG9mIGdvb2dsZSBzZWFyY2ggcmVzdWx0cy4uCgokIG9kIC1kIC9kZXYvdXJhbmRvbSB8IGhlYWQKMDAwMDAwMCAgICAgNjAyMTUgICAyODc3OCAgIDI5MjI3ICAgMjg1NDggICA2MjY4NiAgIDQ1MT…...

算法通关村14关 | 堆在数组中找第k大的元素应用

1. 在数组中找第k大元素 题目 LeetCode215&#xff1a;给定整数数组nums和整数k&#xff0c;请返回数组中第k个最大的元素&#xff0c; 思路 解题思路用三个&#xff0c;选择法&#xff0c;堆查找和快速排序。 我们选择用大堆小堆解决问题&#xff0c;“找最大用小堆&#xff…...

Unity 顶点vertices,uv,与图片贴图,与mesh

mesh就是组成3d物体的三角形们。 mesh由顶点组成的三角形组成&#xff0c;三角形的大小 并不 需要一样&#xff0c;由顶点之间的位置决定。 mesh可以是一个或者多个面。 贴图的原点在左下角&#xff0c;uv是贴图的坐标&#xff0c;数量和顶点数一样&#xff08;不是100%确定…...

Shell编程之函数

目录 基本概念 自定义函数 系统函数 1.read 2.basename 3.dirname 基本概念 将一段代码组合封装在一起实现某个特定的功能或返回某个特定的值&#xff0c;然后给这段代码取个名字&#xff0c;也就是函数名&#xff0c;在需要实现某个特定功能的时候直接调用函数名即可。 函…...

10.物联网LWIP之TCP状态转变

一。TCP状态机 1.青粗线&#xff1a;理想TCP状态转变&#xff08;服务器视角下&#xff09; 2.虚线&#xff1a;被动TCP状态转变&#xff08;服务器视角下&#xff09; 3.细实线&#xff1a;不经常出现的TCP状态转变&#xff08;类似于边界处理&#xff09; 1.青粗线解释--》服…...

Img标签的src地址自动拼接本地域名(localhost:8080)导致图片不显示问题

摘要&#xff1a;做Vueelement ui项目的时候&#xff0c;发现使用element ui的upload上传图片时&#xff0c;不显示的问题。我项目的图片是上传到七牛云&#xff0c;长传成功后返回存储在七牛云中的地址。后面发现是因为返回的地址是外部地址&#xff0c;需要完整的URL&#xf…...

数据结构入门 — 栈

本文属于数据结构专栏文章&#xff0c;适合数据结构入门者学习&#xff0c;涵盖数据结构基础的知识和内容体系&#xff0c;文章在介绍数据结构时会配合上动图演示&#xff0c;方便初学者在学习数据结构时理解和学习&#xff0c;了解数据结构系列专栏点击下方链接。 博客主页&am…...

Unity Android 之 在Unity 中引入 OkHttp的操作注意(OKHttp4.xx- kotlin 的包)简单记录

Unity Android 之 在Unity 中引入 OkHttp的操作注意(OKHttp4.xx- kotlin 的包)简单记录 目录 Unity Android 之 在Unity 中引入 OkHttp的操作注意(OKHttp4.xx- kotlin 的包)简单记录 一、简单介绍 二、OKHttp 4.xx 的 SDK 封装 aar 给 Unity 的使用注意 三、附录 OKHttp 的…...

内嵌功能强大、低功耗STM32WB55CEU7、STM32WB55CGU7 射频微控制器 - MCU, 48-UFQFN

一、概述&#xff1a; STM32WB55xx多协议无线和超低功耗器件内嵌功能强大的超低功耗无线电模块&#xff08;符合蓝牙 低功耗SIG规范5.0和IEEE 802.15.4-2011标准&#xff09;。该器件内含专用的Arm Cortex -M0&#xff0c;用于执行所有的底层实时操作。这些器件基于高性能Arm …...

【测试】笔试03

文章目录 1. 哪种测试模型把测试过程作为需求分析、概要设计、详细设计及编码之后的阶段&#xff08; &#xff09;2. 在下面所列举的逻辑测试覆盖中&#xff0c;测试覆盖最强的是&#xff1f;3. 网络管理员编写了shell程序prog1.sh,测试时程序死循环无法结束,可以通过下列方式…...

JavaScript的while和for循环

一、循环语句 1.认识循环 在开发中我们经常需要做各种各样的循环操作&#xff1a; 比如把一个列表中的商品、歌曲、视频依次输出进行展示&#xff1b;比如对一个列表进行累加计算&#xff1b;比如运行相同的代码将数字 1 到 10 逐个输出&#xff1b; 循环 是一种重复运行同…...

mqtt安卓客户端

1.MQTT&#xff08;消息队列遥测传输协议&#xff09;&#xff0c;是一种基于 发布/订阅 &#xff08;publish/subscribe&#xff09;模式的"轻量级"通讯协议&#xff0c; 该协议构建于TCP/IP协议上 。MQTT最大优点在于&#xff0c;可以以极少的代码和有限的带宽&…...

pdf怎么删除其中一页?

pdf怎么删除其中一页&#xff1f;现在&#xff0c;pdf文件已经深入影响着我们的工作和学习&#xff0c;如果你是一个上班族&#xff0c;那么几乎每天都会使用到pdf格式的电脑文件。当我们阅读一个页数众多的PDF文件时&#xff0c;可能会发现实际上只需要其中的一小部分内容。很…...

10.Redis 渐进式遍历

Redis 渐进式遍历 渐进式遍历scan 渐进式遍历 keys 命令一次性的把整个redis中所有的key都获取到&#xff0c;keys *但这个操作比较危险&#xff0c;可能会一下子得到太多的key,阻塞 redis 服务器。 通过渐进式遍历&#xff0c;就可以做到&#xff0c;既可以获取到所有的 key&…...

字符函数和字符串函数(2)

目录 memcpy memmove memcmp memcpy void * memcpy ( void * destination, const void * source, size_t num ); 1.函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。 2.这个函数在遇到 \0 的时候并不会停下来。 3.如果source和destination有…...

目录扫描+JS文件中提取URL和子域+403状态绕过+指纹识别(dirsearch_bypass403)

dirsearch_bypass403 在安全测试时&#xff0c;安全测试人员信息收集中时可使用它进行目录枚举&#xff0c;目录进行指纹识别&#xff0c;枚举出来的403状态目录可尝试进行绕过&#xff0c;绕过403有可能获取管理员权限。不影响dirsearch原本功能使用 运行流程 dirsearch进行…...

【UE 材质】常用向量运算节点——点积、叉积、归一化

目录 一、点积 二、叉积 三、归一化 一、点积 点积&#xff0c;也称为内积或数量积&#xff0c;是一种用于计算两个向量之间关系的操作。对于两个三维向量 A&#xff08;a1,a2,a3&#xff09;和 B(b1,b2,b3)&#xff0c;它们的点积可以用以下公式表示&#xff1a; ABa1​⋅…...

音视频 ffmpeg命令提取PCM数据

提取PCM ffmpeg -i buweishui.mp3 -ar 48000 -ac 2 -f s16le 48000_2_s16le ffmpeg -i buweishui.mp3 -ar 48000 -ac 2 -sample_fmt s16 out_s16.wav ffmpeg -i buweishui.mp3 -ar 48000 -ac 2 -codec:a pcm_s16le out2_s16le.wav ffmpeg -i buweishui.mp3 -ar 48000 -ac 2 -f…...

【MySQL】实现可扩展性:构建高性能的系统

什么是可扩展性&#xff1f;可扩展性的好处扩展方式纵向扩展&#xff08;Scaling Up&#xff09;横向扩展&#xff08;Scaling Out&#xff09; 总结 &#x1f4af;感谢 &#x1f496; 什么是可扩展性&#xff1f; 可扩展性是指系统能够在需要时轻松地适应更多的工作负载和资源…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...

Python 训练营打卡 Day 47

注意力热力图可视化 在day 46代码的基础上&#xff0c;对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

9-Oracle 23 ai Vector Search 特性 知识准备

很多小伙伴是不是参加了 免费认证课程&#xff08;限时至2025/5/15&#xff09; Oracle AI Vector Search 1Z0-184-25考试&#xff0c;都顺利拿到certified了没。 各行各业的AI 大模型的到来&#xff0c;传统的数据库中的SQL还能不能打&#xff0c;结构化和非结构的话数据如何和…...

【WebSocket】SpringBoot项目中使用WebSocket

1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖&#xff0c;添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...