Vert.x,Web - Router

news2024/11/26 3:57:06

Vert.x-Web简介

Vert.x-Web是用于构建Web应用程序的一系列模块,可以用来构建经典的服务端Web应用, RESTful应用, 实时的(服务端推送)Web应用,或任何您所能想到的Web应用类型。Vert.x Web的设计是强大的,非侵入式的,并且是完全可插拔的,可以只使用您需要的部分。 Vert.x Web不是一个Web容器。

要使用Vert.x Web,需要添加"vertx-web"依赖:

<dependency>
	<groupId>io.vertx</groupId>
	<artifactId>vertx-web</artifactId>
	<version>4.5.10</version>
</dependency>

与NetServer类似,要创建一个Web服务器,首先需要创建HTTP服务器实例,并设置请求处理器,响应http请求。

public static void main(String[] args) {
	Vertx vertx = Vertx.vertx();
	HttpServer server = vertx.createHttpServer();
	// HttpServer requestHandler(Handler<HttpServerRequest> handler);
	server.requestHandler(request -> {
		HttpServerResponse response = request.response();
		response.putHeader("content-type", "text/plain");
		response.end("Hello World!");
	}).listen(8080);
}

http服务器实例监听8080端口,并设定了一个Handler<HttpServerRequest>请求处理器,当有http请求时候,服务器实例将以HttpServerRequest做为事件,传递给我们的编写处理器处理。通过浏览器访问http://localhost:8080/,将获取响应“Hello World!”。

Vert.x Web基本概念

在以上案例中,所有的http请求返回的都是"Hello World!"。实际场景中,不同的http请求表示请求/操作不同的资源或者页面,我们需要区分并进行相应的响应。将所有请求都放在一个处理器(方法)处理是不合适的,所以,Vert.x Web引入了Router (路由器)概念,使整个编程逻辑更简单。

Router由多个路由Route(路由)的对象组成。Router根据传入http请求查找匹配的路由,并将请求传递给该路由来处理。

路由可以持有一个与之关联的处理器(Handler <RoutingContext> )用于处理请求。一个路由可以有多个处理器,开发者可以根据需要, 在一个处理器里结束这个响应(返回内容给客户端)或者把请求传递给下一个匹配的处理器(继续处理)。

传递给处理器的对象是RoutingContext(路由上下文),每个http请求都有一个唯一对应的RoutingContext实例(对象),RoutingContext在对应的(路由)处理器间传递。 RoutingContext包含了请求(HttpServerRequest),响应(HttpServerResponse)等有用信息。并保存http请求生命周期内您想在多个处理器之间共享的数据。

我们来看一个http请求路由的案例:

public static void main(String[] args) {
	Vertx vertx = Vertx.vertx();
	HttpServer server = vertx.createHttpServer();
	Router router = Router.router(vertx);
	
	 配置一个路由, 匹配访问/some/path路径的HTTP请求
	Route route = router.route("/some/path/");
	 为该路由关联一个Handler(处理器)。
	route.handler(routingContext -> {
		// 路由上下文的HttpServerRequest对象可以获取该请求的具体信息(例如请求参数)
		HttpServerRequest request = routingContext.request();
		String pageValue = request.getParam("page");
		String limitValue = request.getParam("limit");
		// 路由上下文的HttpServerResponse对象可以用来响应该请求。
		HttpServerResponse response = routingContext.response();
		
		// 开启分块响应
		response.setChunked(true); 
		response.putHeader("content-type", "text/plain");
		response.write(request.method().name() + " " + request.absoluteURI());
		response.write(" page=" + pageValue + "; limit=" + limitValue);
		response.end(); // 结束响应处理, 返回给客户端
	});
	server.requestHandler(router).listen(8080);
}

当通过浏览器访问http://127.0.0.1:8080/some/path?page=2&limit=10,浏览器将返回以下文本信息:

GET http://127.0.0.1:8080/some/path?page=2&limit=10 page=2; limit=10

上例中,response.end()用于结束响应处理,只有调用end方法后,响应才会返回给浏览器,如果我们注释掉该方法,那么浏览器会一直"打转",处于等待响应状态。需要注意的是,我们这里使用了分段写入(调用了response.write方法),需要调用’response.setChunked(true)'开启分段写入,否则会抛异常。

一个Route支持多个处理器,如果一个请求还需要其它处理器继续处理,需要注释掉response.end(),并调用routingContext.next(),这时会以RoutingContext实例做为参数,调用其它(如果有)处理器继续处理,但不要忘记,在最后需要执行end方法,结束处理,将响应返回给浏览器。

基于路径路由

现实中,最常用的是根据不同的路径来访问不同的页面/资源。Vert.x支持多种基于路径的路由匹配规则。

基于精确路径的路由

只匹配路径一致的请求。例如:

router.route("/some/path/").handler(routingContext -> {
	...
});

匹配案例说明:

### 指定匹配"/some/path/"
匹配:
http://127.0.0.1:8080/some/path/
http://127.0.0.1:8080/some/path/?page=1&limit=10
不匹配:
http://127.0.0.1:8080/some/path
http://127.0.0.1:8080/some/path/subpath

### 指定匹配"/some/path"
匹配:
http://127.0.0.1:8080/some/path/
http://127.0.0.1:8080/some/path
不匹配:
http://127.0.0.1:8080/some/path/subpath

基于路径前缀的路由

与基于精确路径的路由设置方法类似,只是指定路径以*作为结尾, 例如:

Route route = router.route("/some/path/*");

匹配案例:

### 指定匹配/some/path/*
匹配:
http://127.0.0.1:8080/some/path/
http://127.0.0.1:8080/some/path
http://127.0.0.1:8080/some/path/pag.html
http://127.0.0.1:8080/some/path/subpath

不匹配:
http://127.0.0.1:8080/some/patha
http://127.0.0.1:8080/some/patha/
http://127.0.0.1:8080/some/patha/page.html

基于正则表达式的路由

通过router.routeWithRegex(“regex”)或者router.route().pathRegex(“regex”)方法基于正则表达式匹配请求。

Route route = router.route().pathRegex(".*foo");
Route route = router.routeWithRegex(".*foo");

匹配案例:

### 指定正则表达式".*foo",匹配foo结尾的路径。
匹配:
http://127.0.0.1:8080/foo
http://127.0.0.1:8080/foo?page=1&limit=10
http://127.0.0.1:8080/some/foo
http://127.0.0.1:8080/some/xfoo

不匹配:
http://127.0.0.1:8080/foo/
http://127.0.0.1:8080/foO
http://127.0.0.1:8080/foo/page.html

获取路径参数

对于Restfull API,我们常常需要获取路径传递的参数。

通过路径占位符获取

Vert.x Web可以通过占位符声明路径参数,并通过pathParam方法获取。占位符由 : 和参数名构成,参数名由字母,数字和下划线构成。

// GET /api/v2/ob/clusters/{id}/zones
router.route("/api/v2/ob/clusters/:id/zones").handler(routingContext -> {
	String clusterId = routingContext.pathParam("id");
	HttpServerResponse response = routingContext.response();
	response.putHeader("content-type", "text/plain; charset=utf-8");
	response.end("显示集群编号为" + clusterId + "的Zone列表。");
});

浏览器访问http://localhost:8080//api/v2/ob/clusters/123/zones,浏览器将返回"显示集群编号为123的Zone列表。"

通过正则表达式捕获组获取

复杂的路径可以通过正则表达式匹配,并通过正则表达式捕获组来获取路径参数。例如:

router.routeWithRegex("\\/([^\\/]+)\\/([^\\/]+)").handler(routingContext ->{
	  String group0 = routingContext.pathParam("param0"); //第一个捕获组值
	  String group1 = routingContext.pathParam("param1"); //第二个捕获组值,以此类推...
	  //注意区别: url参数是通过HttpServerRequest.getParam(...)获取的
	  // HttpServerRequest request = routingContext.request();
	  // String page = request.getParam("page");
});

匹配案例:

### 指定正则表达式"\\/([^\\/]+)\\/([^\\/]+)",匹配任意/xxx/xxx格式的路径。

匹配:
http://127.0.0.1:8080/emp/1    => group0 = emp, group1 = 1
http://127.0.0.1:8080/foo/page1.html  => group0 = foo, group1 = page1.html
http://127.0.0.1:8080/foo/page1?page=1&limit=12 => group0 = foo, group1 = page1
不匹配:
http://127.0.0.1:8080/api/emp/1
http://127.0.0.1:8080/foo/page1/
...

使用序号参数名在某些场景下可能会比较麻烦。 亦可在正则表达式路径中使用命名的捕捉组。

router.routeWithRegex("\\/(?<productType>[^\\/]+)\\/(?<productID>[^\\/]+)").handler(ctx -> {
	// 通过组名替代了之前的param0, param1, param2, ...
	String productType = ctx.pathParam("productType"); 
	String productID = ctx.pathParam("productID");
});

基于HTTP方法的路由

Route默认会匹配所有的HTTP方法,在编写Restfull API时,通常会通过HTTP 请求方法来代表对资源的不同的操作,例如:

GET - 从服务器获取资源。用于请求数据而不对数据进行更改。
POST - 向服务器发送数据以创建新资源。
PUT - 向服务器发送数据以更新现有资源。
DELETE - 从服务器删除指定的资源。
...

Route是可以基于HTTP请求方法路由请求的,例如:

// api/v2/ob/clusters
// router.route().method(HttpMethod.POST).path("/api/v2/ob/clusters").handler(routingContext -> { //等效写法
router.route(HttpMethod.POST, "/api/v2/ob/clusters").handler(routingContext -> {
	HttpServerResponse response = routingContext.response();
	response.putHeader("content-type", "application/json; charset=utf-8");
	JsonObject resultObj = new JsonObject().put("data", "json array of clusters");
	response.end(resultObj.toString());
});

我们通过Postman, 以POST方式访问http://127.0.0.1:8080/api/v2/ob/clusters,可以获取正确结果:

{
    "data": "json array of clusters"
}

如果我们以POST以外的方法访问,如GET,则会报错"405 Method Not Allowed"。

可以组合路由匹配多种方法,例如:

router.route()
	.method(HttpMethod.POST)
	.method(HttpMethod.GET)
	.path("/api/v2/ob/clusters")
	.handler(routingContext -> {
		// 同时路由匹配POST, GET方法
});

基于MIME类型的路由

HTTP可以通过content-type请求头,指定请求体(body)的MIME类型;通过accept请求头,用于表示客户端可以接受响应的MIME类型。

Route可以通过consumes方法配置基于content-type请求头的路由匹配,通过produces方法配置基于accept请求头的路由匹配。

// 必须设置BodyHandler(请求体处理器),才能接收到客户端提交的请求体
router.route().handler(BodyHandler.create()); 

router.route()
	.path("/some/path2")
	.consumes("text/html")
	.consumes("text/plain")
	.handler(routingContext -> {
		RequestBody requestBody = routingContext.body();
		HttpServerResponse response = routingContext.response();
		response.putHeader("content-type", "text/plain");
		String body = Objects.isNull(requestBody) ? "null": requestBody.asString();
		response.end("Request Body: " +  body);
});

我们通过PUT方法访问http://127.0.0.1:8080/some/path2,并指定请求体和请求体类型,页面可以正常处理请求。
设定请求体(Body)的内容:

Postman会根据你的请求头指定Content-Type,也可以修改。

如果修改"Content-Type"为"application/json",那么会报错"415 Unsupported Media Type"。

类似的,通过produces方法基于请求头accept进行匹配:

router.route()
	.path("/some/path2")
	.produces("text/html")
	.handler(routingContext -> {
		RequestBody requestBody = routingContext.body();
		HttpServerResponse response = routingContext.response();
		response.putHeader("content-type", "text/plain");
		String body = Objects.isNull(requestBody) ? "null": requestBody.asString();
		response.end("Request Body: " +  body);
});

我们通过浏览器或者Postman访问的http://127.0.0.1:8080/some/path2都是正常的,因为默认的accept请求头包含允许所有类型*,*
在这里插入图片描述
如果使用Postman,强制指定Accept请求的值为"application/json",那么将返回"406 Not Acceptable"。

组合多个路由条件

路由条件是可以组合的:

router.route()
	.path("/some/path3")
	.method(HttpMethod.GET)
	.consumes("text/plain")
	.produces("text/html")
	.handler(routingContext -> {
		//...
	});

路由顺序

默认情况下Route按照其加入到Router的顺序进行匹配。路由器会逐级检查每条路由是否匹配,如果匹配的话,该Route的处理器将被调用。

前提是处理器中不结束相应(不调用HttpServerResponse的end方法),并调用RoutingContext的next方法。

router.route().setName("/*").handler(routingContext -> {
	HttpServerResponse response = routingContext.response();
	response.setChunked(true); 
	response.putHeader("content-type", "text/plain");
	response.write("Route: " + routingContext.currentRoute().getName() + " handle ...\n");
	routingContext.next();
});

router.route().setName("/some/*").path("/some/*").handler(routingContext -> {
	HttpServerResponse response = routingContext.response();
	response.write("Route: " + routingContext.currentRoute().getName() + " handle ...\n");
	routingContext.next();
});

router.route().path("/some/path1").handler(routingContext -> {
	HttpServerResponse response = routingContext.response();
	response.write("Route: " + routingContext.currentRoute().getName() + " handle ...\n");
	routingContext.next();
});

router.routeWithRegex("/some/path.*").handler(routingContext -> {
	HttpServerResponse response = routingContext.response();
	response.write("Route: " + routingContext.currentRoute().getName() + " handle ...\n");
	response.end();
});

当浏览器访问http://127.0.0.1:8080/some/path1,上例的4个路由都能匹配,他们将按加入Router向后顺利匹配。遇到结束响应处理(response.end)后结束匹配。浏览器响应的结果如下:

Route: /* handle ...
Route: /some/* handle ...
Route: /some/path1 handle ...
Route: /some/path.* handle ...

如果4个路由的处理器都直接结束响应处理,那么只会匹配最先加入的一条路由。

可以通过Route的order方法自定路由顺序或者说优先级。Route在创建时被分配的顺序与它们被添加到Router的顺序相对应:第一个Route编号为0,第二个Route编号为1,以此类推。通过给Route指定order您可以覆盖默认值,order可以为负值。例如:

router.route().order(-1).setName("/*").handler(routingContext -> { /* ...*/});
router.route().order(3).setName("/some/*").path("/some/*").handler(routingContext -> {/* ...*/});
router.route().order(1).path("/some/path1").handler(routingContext -> {/* ...*/});
router.routeWithRegex("/some/path.*").order(Integer.MAX_VALUE).handler(routingContext -> {/* ...*/});

指定优先级后,将以优先级小到大的顺序匹配:

Route: /* handle ...
Route: /some/path1 handle ...
Route: /some/* handle ...
Route: /some/path.* handle ...

重新路由

可以通过RoutingContext指定匹配一个路由(重新路由)。

router.route().path("/some/path").handler(ctx -> ctx.reroute("/some/path/B"));
router.route().path("/some/path/B").handler(ctx -> ctx.response().end("/some/path/B"));

浏览器访问http://127.0.0.1:8080/some/path,将返回"/some/path/B"。在错误处理时常会用到,如访问不存在的页面时候,自动跳转到主页或者错误页面。

错误处理

路由器(匹配)错误处理

如果没有路由符合任何特定请求,则Vert.x-Web将根据匹配失败发出错误消息(HTTP响应状态码 )。

HTTP响应状态码用来表明特定HTTP请求是否成功完成。具体代码可以参考: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status

当浏览器访问一个不存在的路径(例如 http://127.0.0.1:8080/some/notfound),这时候HTTP服务器会返回status code 404,同时Vert.x Web默认错误处理器会响应"Resource not found"给浏览器,可以通过Router的errorHandler方法来自定义错误处理:

router.errorHandler(404, routingContext -> {
	HttpServerResponse response = routingContext.response();
	response.putHeader("content-type", "text/html; charset=utf-8");
	response.end("<html><body><h1>啊哦~你想找的内容离你而去了哦(404, 内容不存在)</h1></body></html>");
});

路由错误处理

当路由关联的处理器抛出未捕获的异常,Vertx.x Web的默认会返回状态码:500,页面返回“Internal Server Error”,例如:

router.route().path("/some/path4").handler(routingContext -> {
	HttpServerRequest request = routingContext.request();
	String pageValue = request.getParam("page");
	int page = Integer.parseInt(pageValue); // 整数解析错误会抛出RuntimeException,而且处理器没有try ... catch捕获异常。
	HttpServerResponse response = routingContext.response();
	response.end("current page: " +  page);
});

浏览器访问: http://127.0.0.1:8080/some/path4,就会返回默认错误页面,服务器端日志系统输出:

2024-10-10 08:56:01 [严重] Unhandled exception in router
java.lang.NumberFormatException: null ⇒ 因为请求参数page为空。
	at java.base/java.lang.Integer.parseInt(Integer.java:614)
	at java.base/java.lang.Integer.parseInt(Integer.java:770)
	at vertx.web.Web2.lambda$0(Web2.java:17)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1285)
	...

可以通过Route的failureHandler方法设置路由错误处理器:

router.route().path("/some/path4").handler(routingContext -> {
	// ...
}).failureHandler(routingContext ->{
	Throwable exception = routingContext.failure();
	int statusCode = routingContext.statusCode();
	 服务器记录日志
	HttpServerRequest request = routingContext.request();
	String method = request.method().name();
	String uri = request.absoluteURI();
	LOGGER.log(Level.SEVERE, method + " " + uri + ", statusCode: " + statusCode, exception);
	
	 返回错误信息
	HttpServerResponse response = routingContext.response();
	response.setStatusCode(statusCode); //必须设置, 否则200 OK
	// response.setStatusMessage(exception.getMessage()); // 可覆盖, 默认是statusCode对应的错误信息。

	// {"error":{"code":500,"message":"Exception Messages Here..."}}
	JsonArray errorArray = new JsonArray()
			.add(new JsonObject().put("code", statusCode))
			.add(new JsonObject().put("message", exception.getMessage()));
	JsonObject respObj = new JsonObject().put("error", errorArray);
	response.end(respObj.toString());
	// response.end(); // 也可不返回任何错误信息给客户端,但记得结束响应。
});

如果浏览器访问: http://127.0.0.1:8080/some/path4?page=haha,那么请求状态(status code)为500,浏览器响应信息为:

{"error":[{"code":500},{"message":"For input string: \"haha\""}]}

服务器端的日志也更详细,包含用户请求信息:

2024-10-10 09:16:20 [严重] GET http://127.0.0.1:8080/some/path4?page=haha, statusCode: 500  ==>
java.lang.NumberFormatException: For input string: "haha"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.base/java.lang.Integer.parseInt(Integer.java:652)
	at java.base/java.lang.Integer.parseInt(Integer.java:770)
	at vertx.web.Web1.lambda$4(Web1.java:90)
	...

Vert.x Web也自带了路由错误处理器(ErrorHandler):

router.route().path("/some/path4").handler(routingContext -> {
	//...
}).failureHandler(ErrorHandler.create(vertx));

ErrorHandler服务器端不记录异常,浏览器返回"An unexpected error occurred 500 Internal Server Error"。

错误处理器也是根据路由条件匹配的,例如:

// 匹配所有的路由条件,所有的路由处理器异常都会触发该错误处理器。
router.route().failureHandler(routingContext ->{ /* ... */});
// 路径为/some/*的路由的错误处理器。
router.route().path("/some/*").failureHandler(routingContext ->{ /* ... */});

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

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

相关文章

电影选票选座系统|影院购票|电影院订票选座小程序|基于微信小程序的电影院购票系统设计与实现(源码+数据库+文档)

电影院订票选座小程序 目录 基于微信小程序的电影院购票系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户功能实现 2、管理员功能实现 &#xff08;1&#xff09;影院信息管理 &#xff08;2&#xff09;电影信息管理 &#xff08;3&#xff09;已完成…

ComfyUI 使用 LoRA 极简工作流

前言 ComfyUI的LoRA工作流相信大家都不陌生。开发者提供了大量基于默认节点功能搭建的工作流&#xff0c;其中就包括了如何使用LoRA。 仅仅使用一个LoRA也许还比较简单。只需要在基本工作流的基础上&#xff0c;再添加一个LoRA加载器的节点即可。如果要添加多个LoRA&#xff…

[C++ 核心编程]笔记 3 引用做函数参数

2.1 引用的基本使用 作用: 给变量起别名 语法: 数据类型 &别名 原名 2.2 引用注意事项 引用必须初始化引用在初始化后&#xff0c;不可以改变 #include<iostream> using namespace std;int main() {//引用基本语法int a 10;//创建引用int& b a;cout <&…

PHP智慧餐饮新风尚点餐系统

智慧餐饮新风尚点餐系统 —— 美食与科技的完美碰撞 &#x1f37d;️ 开篇&#xff1a;智慧餐饮的崛起 在快节奏的现代生活中&#xff0c;智慧餐饮正逐渐成为我们日常的一部分。随着科技的飞速发展&#xff0c;餐饮行业也在不断创新&#xff0c;力求为顾客提供更加便捷、高效…

中国象棋,Android小游戏开发

中国象棋&#xff0c;Android小游戏开发 A. 项目描述 本项目设计并开发了一款易于上手又富有挑战性的中国象棋应用。 用户界面与体验&#xff1a; 简洁与直观的设计&#xff0c;确保无论是新手还是老手&#xff0c;都能轻松使用。象棋主界面展示清晰的棋盘和操作选项&#x…

什么是DHCP Snooping?到底工作在第几层?

号主&#xff1a;老杨丨11年资深网络工程师&#xff0c;更多网工提升干货&#xff0c;请关注公众号&#xff1a;网络工程师俱乐部 中午好&#xff0c;我的网工朋友 对于企业和机构而言&#xff0c;保证内部网络的安全稳定运行不仅是日常运营的基础&#xff0c;更是防止敏感信息…

ChinaER:重塑跨境互联新体验

中国联通国际公司产品之 ChinaER&#xff1a;打造高效、安全的微软 Azure 云跨境互联服务 在全球化的数字时代&#xff0c;企业对于云计算的需求日益增长&#xff0c;尤其是跨境互联服务&#xff0c;成为企业拓展海外市场、实现全球业务协同的关键。中国联通国际公司&#xff…

整合Ollama实现本地LLMs调用

前言 近两年AIGC发展的非常迅速&#xff0c;从刚开始的只有ChatGPT到现在的很百家争鸣。从开始的大参数模型&#xff0c;再到后来的小参数模型&#xff0c;从一开始单一的文本模型到现在的多模态模型等等。随着一起进步的不仅仅是模型的多样化&#xff0c;还有模型的使用方式。…

JavaScript 第2章 基本语法

第二部分&#xff1a;基本语法 第2章&#xff1a;基本语法 变量声明&#xff1a;let, const, var 变量声明 var&#xff1a;全局或函数级作用域&#xff0c;可重复声明同名变量。let&#xff1a;块级作用域&#xff0c;不可重复声明同名变量。const&#xff1a;块级作用域&…

WGCNA加权基因共表达网络多步法分析学习

之前笔者介绍过一步法的分析的流程&#xff1a; WGCNA加权基因共表达网络一步法分析学习 https://mp.weixin.qq.com/s/2Q37RcJ1pBy_WO1Es8upIg 建议先看一下之前的推文&#xff0c;了解一下WGCNA的基础原理。 这次就来介绍一下多步法 分析步骤&#xff1a; 1.导入 rm(list…

安全工具 | 搭建带有 Web 仪表板的Interact.sh

介绍 Interactsh 是一个用于检测带外交互的开源工具。它是一种旨在检测导致外部交互的漏洞的工具。本文将主要介绍在子域上设置私有 Interact.sh 服务器以及部署其 Web 应用程序。只需一个 AWS EC2 或 VPS 实例和一个域。 要求 •具有静态IP的AWS EC2 / VPS •拥有自己的域…

A CXL-Powered Database System: Opportunities and Challenges——论文阅读

ICDE 2024 Paper CXL论文阅读笔记整理 背景 Compute Express Link&#xff08;CXL&#xff09;是处理器和设备&#xff08;如内存缓冲区&#xff09;之间的开放式行业标准互连协议&#xff0c;基于CXL的内存架构如图1所示&#xff0c;拥有高带宽、低延迟以及对一致性和内存语…

Java 的数据结构整理(整合版)

Java 的数据结构整理&#xff08;整合版&#xff09; 一、数据输入输出 https://www.runoob.com/java/java-scanner-class.html 这部分是为了预防 leetcode 刷习惯了&#xff0c;忘记怎么处理输入输出的问题 数据输入 Java的数据输入和 C 相比非常繁琐&#xff0c;因此大多…

双十一有哪些不容错过的电子产品?看完不信你还不心动!

2024年双十一购物庆典即将上演&#xff0c;各大电商将携众多精品数码产品亮相&#xff0c;涵盖最新智能手机、笔记本、平板及智能手表等。这些产品集时尚外观与高端性能于一身&#xff0c;旨在满足用户多元化需求&#xff0c;如提升工作效率、享受娱乐时光、优化出行体验等。为…

中国上市药品目录集数据库查询方法-支持数据下载

《中国上市药品目录集》由国家食品药品监督管理总局以数据库形式发布并实时更新&#xff0c;由CDE负责日常维护和管理。《中国上市药品目录集》收录了在中国批准上市的创新药、改良型新药、化学药品新注册分类的仿制药以及通过质量和疗效一致性评价的药品的具体信息。这个目录集…

jmeter入门: 安装

1. download Apache JMeter - Download Apache JMeter 2. 解压jmeter包 3. 安装插件Install :: JMeter-Plugins.org 下载jar包&#xff0c;放到lib/ext目录 4. 打开jmeter &#xff08;可以看到插件管理选项&#xff09;

Autoware.universe-红绿灯识别程序调试

1、环境 gpu&#xff1a;RTX3070 操作系统&#xff1a;ubuntu 22.04 显卡驱动支持的最高cuda版本&#xff1a;cuda 12.6 cuda运行版本&#xff1a;cuda 12.3&#xff08;注&#xff1a;cuda运行版本需要小于显卡驱动支持的最高cuda版本&#xff09; 2、仓库 https://gitee.com/…

UR-vnc如何卸载和重新安装

urvnc资源下载https://download.csdn.net/download/xiaoding_ding/87610583 场景&#xff1a;有时候我们安装ur机器人vnc的时候没有完全安装完就拔了U盘&#xff0c;会出现一直登陆一直提示密码错误。这个时候就需要我们卸载这个vnc,然后重新进行安装了。 如果不卸载会提示已…

人工智能:超越人类思维的边界

人工智能在追求真理和正确性方面面临诸多挑战&#xff0c;而人类的思维方式在其中扮演着重要角色。然而&#xff0c;新一代的人工智能正通过更具实验性的方法&#xff0c;力图在机器学习领域实现远超人类的突破。 AlphaGo&#xff1a;人工智能的里程碑 提及人工智能的突破&…

全闪 SDS 一体机提供 FC 能力承载医院核心业务

邹平市人民医院使用 X3000 SDS 一体机组建分布式存储集群&#xff0c;通过 FC 接口 与 VMware 集群连接&#xff0c;以全闪池承载核心业务&#xff0c;对象存储承载 PACS 数据&#xff0c;实现存储架构的升级改造。 “新医改”的不断推进&#xff0c;对医院的运营管理、服务质…