Geoserver源碼解讀四 REST服務

文章目錄

文章目錄

一、概要

二、前置知識點-FreeMarker

三、前置知識點-AbstractHttpMessageConverter

3.1 描述

3.2 應用

四、前置知識點-AbstractDecorator

4.1描述

4.2 應用

五、工作空間查詢解讀

5.1 模板解讀

5.2 請求轉換器解讀


一、概要

關于geoserver的rest服務,其實官網有一個簡單的描述,此處不多搬運詳情可以查看它官網描述(點我),但是需要重點了解的是最新的GeoServer是使用SpringMVC來實現的REST服務,拋棄了Restlet。GeoServer擴展之REST_geoserver過時了-CSDN博客?從GeoServer2.12版(2017)開始采用的SpringMVC,?它的Wiki中也做了個簡單描述,但是開發文檔沒有更新,重要的事情說兩遍開發文檔沒有更新。所以官網描述看看就可以了,不用跟著它的指引做。?本文著重從源碼角度梳理整個rest服務的流程

二、前置知識點-FreeMarker

在上一篇文章中看到geoserver的模板框架是FreeMarker

主體框架spring(不是spring boot)
UI框架Wicket(類似jsp)
通信框架(前后臺交互)Servlet
地理處理框架GeoTools
模板框架FreeMarker

這個東西主要就是用于格式化REST接口

用法的話參照下面的代碼(AI生成的,可能細節上有問題,看看即可)

1.環境配置

Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
cfg.setDirectoryForTemplateLoading(new File("templates")); // 設置模板目錄
cfg.setDefaultEncoding("UTF-8"); // 設置默認編碼

2.加載模板

Template template = cfg.getTemplate("example.ftl");

3.數據模型

Map<String, Object> dataModel = new HashMap<>();
dataModel.put("title", "FreeMarker 示例");
dataModel.put("message", "這是一個 FreeMarker 模板!");

4.處理模板

StringWriter out = new StringWriter();
template.process(dataModel, out);
String result = out.toString();
System.out.println(result);

三、前置知識點-AbstractHttpMessageConverter

AbstractHttpMessageConverter?一般與rest 接口聯合使用,用于根據前端需求返回不同格式的結果,就比如工作空間的三種請求方式

3.1 描述

下面是AI(智普清言)生成的,可能細節上有問題,看看即可

AbstractHttpMessageConverter?是 Spring 框架中用于處理 HTTP 請求和響應的轉換的一個抽象類。它為具體的 HTTP 消息轉換器提供了一種模板方法模式,用于將請求體或響應體轉換為 Java 對象,或者將 Java 對象轉換為響應體。

如果你需要自定義一個消息轉換器,你可以擴展這個類,并實現其中的抽象方法。下面是擴展?AbstractHttpMessageConverter?的基本步驟:

  1. 確定支持的媒體類型:在構造函數中設置你的轉換器將支持哪些媒體類型(例如?application/json,?text/xml?等)。

  2. 實現?supports?方法:這個方法需要判斷傳入的 Java 類型是否為你的轉換器所支持的類型。

  3. 實現?read?方法:這個方法負責將請求體轉換為 Java 對象。

  4. 實現?write?方法:這個方法負責將 Java 對象轉換為響應體。

以下是一個簡單的示例,展示了如何創建一個自定義的?AbstractHttpMessageConverter

import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;import java.io.IOException;
import java.nio.charset.Charset;public class CustomMessageConverter extends AbstractHttpMessageConverter<MyObject> {public CustomMessageConverter() {// 設置支持的媒體類型super(new MediaType("application", "custom", Charset.forName("UTF-8")));}@Overrideprotected boolean supports(Class<?> clazz) {// 判斷傳入的類型是否為 MyObject 或其子類return MyObject.class.isAssignableFrom(clazz);}@Overrideprotected MyObject readInternal(Class<? extends MyObject> clazz, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException {// 實現從請求體到 MyObject 的轉換邏輯// ...return new MyObject();}@Overrideprotected void writeInternal(MyObject myObject, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException {// 實現從 MyObject 到響應體的轉換邏輯// ...}
}

在上述代碼中,MyObject?是你希望轉換的目標對象類型。你需要實現?readInternal?和?writeInternal?方法來完成具體的轉換邏輯。

最后,不要忘記將你的自定義轉換器注冊到 Spring 的?HttpMessageConverter?列表中,這通常是通過配置一個?WebMvcConfigurer?來實現的:

@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {converters.add(new CustomMessageConverter());}
}

這樣,當 Spring MVC 處理請求和響應時,就會使用你的自定義轉換器來處理?MyObject?類型的數據。

博客園里面有一篇文章寫的也不錯可以參考(點我)

3.2 應用

在geoserver中,設置轉換器的配置代碼在RestConfiguration

src/rest/src/main/java/org/geoserver/rest/RestConfiguration.java

在applicationContext.xml中可以看到掃描的是整個包下面的類

<?xml version="1.0" encoding="UTF-8"?>
<beans><!-- <mvc:annotation-driven/> --><context:component-scan base-package="org.geoserver.rest"/>
</beans>

當掃描到RestConfiguration時就會自動注冊消息轉換器

import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
/** Configure various aspects of Spring MVC, in particular message converters */
@Configuration
public class RestConfiguration extends WebMvcConfigurationSupport {/** 配置消息轉換器 */@Overrideprotected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {Catalog catalog = (Catalog) applicationContext.getBean("catalog");List<BaseMessageConverter> gsConverters =GeoServerExtensions.extensions(BaseMessageConverter.class);gsConverters.add(new FreemarkerHTMLMessageConverter("UTF-8"));gsConverters.add(new XStreamXMLMessageConverter());gsConverters.add(new XStreamJSONMessageConverter());gsConverters.add(new XStreamCatalogListConverter.XMLXStreamListConverter());gsConverters.add(new XStreamCatalogListConverter.JSONXStreamListConverter());gsConverters.add(new InputStreamConverter());EntityResolver entityResolver = catalog.getResourcePool().getEntityResolver();for (StyleHandler sh : Styles.handlers()) {for (Version ver : sh.getVersions()) {gsConverters.add(new StyleReaderConverter(sh.mimeType(ver), ver, sh, entityResolver));gsConverters.add(new StyleWriterConverter(sh.mimeType(ver), ver, sh));}}if (applicationContext.containsBean("gwcConverter")) {converters.add((HttpMessageConverter<?>) applicationContext.getBean("gwcConverter"));}gsConverters.sort(Comparator.comparingInt(BaseMessageConverter::getPriority));for (BaseMessageConverter converter : gsConverters) {converters.add(converter);}converters.removeIf(Jaxb2RootElementHttpMessageConverter.class::isInstance);converters.add(0, new Jaxb2RootElementHttpMessageConverter());super.addDefaultHttpMessageConverters(converters);}
}

上面的一對轉換器都是針對geoserver一些特定對象的封裝,像workspace、layer、datastore等,最下面那個比較特殊,也是比較常見的一個,它用于將java對象轉換成json或者xml返回給前端

converters.add(0, new Jaxb2RootElementHttpMessageConverter());

比如果當請求工作空間時一般有下面的幾種請求

http://localhost:8080/geoserver/rest/workspaces   (瀏覽器預覽居多)
或
http://localhost:8080/geoserver/rest/workspaces.json (作為前端調用的接口居多)
或
http://localhost:8080/geoserver/rest/workspaces.xml  (作為前端調用的接口居多)

Jaxb2RootElementHttpMessageConverter 轉換器,會根據前端請求的Accept請求頭自動適配出前端需要的格式

其優先級是 格式拼接到請求地址上(http://localhost:8080/gisserver/rest/workspaces.json)大于 請求地址什么都不加 但是header有Accept參數

四、前置知識點-AbstractDecorator

4.1描述

?org.geotools.util.decorate.AbstractDecorator?是 GeoTools 庫中的一個類,它提供了一個基礎實現,用于創建裝飾者模式(Decorator Pattern)的裝飾器。裝飾者模式允許你動態地給一個對象添加額外的職責,而不需要修改其原有的代碼。通俗來說就是子類定義一個delegate變量,在子類方法中直接代用父類的方法,并且這個變量一般是通過依賴注入的,不用單獨的給賦值。

在 GeoTools 中,AbstractDecorator?類是一個抽象類,它實現了?Decorator?接口,并提供了一個構造函數,接受一個要裝飾的對象作為參數。這個被裝飾的對象通常是一個接口的實現,而?AbstractDecorator?類則負責將所有的調用委派給這個對象。

舉例:

import org.geotools.util.decorate.AbstractDecorator;public class MyDecorator extends AbstractDecorator<MyInterface> {public MyDecorator(MyInterface delegate) {super(delegate);}@Overridepublic void doSomething() {// 在調用原有方法之前,可以添加一些額外的邏輯System.out.println("Before doing something");// 調用被裝飾對象的方法delegate.doSomething();// 在調用原有方法之后,也可以添加一些額外的邏輯System.out.println("After doing something");}
}public interface MyInterface {void doSomething();
}public class MyImplementation implements MyInterface {@Overridepublic void doSomething() {System.out.println("Doing something");}
}public class Main {public static void main(String[] args) {MyInterface myImplementation = new MyImplementation();MyDecorator myDecorator = new MyDecorator(myImplementation);myDecorator.doSomething();}
}

4.2 應用

AbstractDecorator?的目的主要是為了理解WorkspaceController類

從源碼中可以看到

public class WorkspaceController extends AbstractCatalogController {private static final Logger LOGGER = Logging.getLogger(WorkspaceController.class);@Autowiredpublic WorkspaceController(@Qualifier("catalog") Catalog catalog) {super(catalog);}
}

擴展的說一下@Autowired是個依賴注入,

構造函數中有一個catalog,但是WorkspaceController是個servlet接口,沒有實例化的地方,構造函數怎么能夠傳過來呢,查看applicationContext.xml可以看到

<alias name="localWorkspaceCatalog" alias="catalog"/>     
<bean id="localWorkspaceCatalog" class="org.geoserver.catalog.impl.LocalWorkspaceCatalog"><constructor-arg ref="advertisedCatalog" />
</bean><bean id="advertisedCatalog" class="org.geoserver.catalog.impl.AdvertisedCatalog"><constructor-arg ref="secureCatalog" /><property name="layerGroupVisibilityPolicy"><bean id="org.geoserver.catalog.LayerGroupVisibilityPolicy.HIDE_NEVER" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/></property>
</bean>   

catalog就這通過applicationContext.xml的配置實現初始情況下就可以構造函數注入進去

再繼續看LocalWorkspaceCatalog?,跟蹤源碼可以看到下面的代碼

public class LocalWorkspaceCatalog extends AbstractCatalogDecorator implements Catalog {}public class AbstractCatalogDecorator extends AbstractDecorator<Catalog> implements Catalog {public AbstractCatalogDecorator(Catalog catalog) {super(catalog);}
}// 反編譯的AbstractDecorator 
public class AbstractDecorator<D> implements Wrapper, Serializable {protected D delegate;public AbstractDecorator(D delegate) {if (delegate == null) {throw new NullPointerException("Cannot delegate to a null object");} else {this.delegate = delegate;}}
}

通過一步步的查看父對象可以看到最終繼承自?org.geotools.util.decorate.AbstractDecorator?,也就是說可以直接用delegate去操作父類的一些操作

五、工作空間查詢解讀

一般來說工作空間的查詢地址是

http://localhost:8080/geoserver/rest/workspaces   (瀏覽器預覽居多)
或
http://localhost:8080/geoserver/rest/workspaces.json (作為前端調用的接口居多)
或
http://localhost:8080/geoserver/rest/workspaces.xml  (作為前端調用的接口居多)

當瀏覽器訪問http://localhost:8080/geoserver/rest/workspaces的servlet代碼位置在如下位置(??引申的說一下,geoserver的rest代碼大多在 gs-restconfig 包下面)

src/restconfig/src/main/java/org/geoserver/rest/catalog/WorkspaceController.java

    @GetMappingpublic RestWrapper workspacesGet() {List<WorkspaceInfo> wkspaces = catalog.getWorkspaces();return wrapList(wkspaces, WorkspaceInfo.class);}

@GetMapping 能看出來它是個普通的spring servlet接口,RestWrapper是對返回結果的一個包裝器,catalog是針對geoserver文件目錄映射出來的一個方法類

查詢結果是這樣的

如果不用包裝器的話返回結果是這樣的

    @GetMapping("/details")public List<WorkspaceInfo> getAllWorkspacesDetails() {List<WorkspaceInfo> workspaces = catalog.getWorkspaces();return workspaces;}

可以看出來如果不用包裝器的話會把查出的數據原封不動的返回出來,而且兼容xml和json,實際不管使用不使用包裝器時上面?三、前置知識點-AbstractHttpMessageConverter?講到Jaxb2RootElementHttpMessageConverter 轉換器都會生效,也就是說一直支持xml個json請求,而當使用包裝器時就用到了另一個模板框架二、前置知識點-FreeMarker

5.1 模板解讀

往下看wrapList源碼

    protected <T> RestWrapper<T> wrapList(Collection<T> list, Class<T> clazz) {return new RestListWrapper<>(list, clazz, this, getTemplate(list, clazz));}

這里面終于找到了一個跟模板相關的東西getTemplate(list, clazz)

在WorkspaceController的基類RestBaseController中找到下面獲取模板的代碼?

    protected Template getTemplate(Object o, Class<?> clazz) {Template template = null;Configuration configuration = createConfiguration(clazz);。。。。。。(此處省略n行代碼)return tryLoadTemplate(configuration, templateName);}

里面的代碼看著沒啥營養我替你們看過了,跟著代碼就能找到模板的位置,也就是這個地方

src/restconfig/src/main/java/org/geoserver/rest/catalog/ftl-templates/workspaces.ftl

<#include "head.ftl">
Workspaces
<ul>
<#list values as w><li><a href="${page.pageURI(w.properties.name + '.html')}">${w.properties.name}</a><#if w.properties.isDefault> [default] 哈哈 </#if></li>
</#list>
</ul>
<#include "tail.ftl">

最后那兩個“哈哈”是我自己加的,瀏覽器訪問可以看到下面效果

如果你那兒是亂碼的可以在頭部模板里面加個<meta charset="UTF-8" />

src/restconfig/src/main/java/org/geoserver/rest/catalog/ftl-templates/head.ftl

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head><title>GeoServer Configuration</title><meta charset="UTF-8" /><meta name="ROBOTS" content="NOINDEX, NOFOLLOW"/>
</head>
<body><#setting number_format="#0.0#">

但是訪問的時候我還有一個疑問,直接訪問是用的模板,但當我訪問json的接口時兒返回結果貌似沒有走這個模板

這是因為什么呢 ,再返回去查getTemplate方法,原因是默認是根據模板名查詢模板的,也就是說根據workspace就能查到模板,換成workspace.json 就不行,如果想要用json類型的模板,就得再定義個workspace.json.flt文件,

if (template == null) template = tryLoadTemplate(configuration, templateName + ".ftl");

總的來說,如果不加干預的話直接請求

http://localhost:8080/geoserver/rest/workspaces

就會使用FreeMarker模板,然后經過轉換器(此處是Jaxb2RootElementHttpMessageConverter 、FreemarkerHTMLMessageConverter、XStreamXMLMessageConverter、XStreamJSONMessageConverter。。。)傳給前端,如果是訪問

http://localhost:8080/geoserver/rest/workspaces.json

的話則會跳過模板直接經過轉換器(此處是Jaxb2RootElementHttpMessageConverter )然后傳給前端

到這里FreeMarker的框架算是基本上梳理完了 ,感覺就像是個放大版的StringBuilder。

5.2 請求轉換器解讀

看了前面描述的三、前置知識點-AbstractHttpMessageConverter?可以知道在查詢完之后會執行一次查詢結果的轉換操作

再次看查詢工作空間的代碼

    @GetMappingpublic RestWrapper workspacesGet() {List<WorkspaceInfo> wkspaces = catalog.getWorkspaces();return wrapList(wkspaces, WorkspaceInfo.class);}

🔎 下鉆查看wrapList的代碼如下

    protected <T> RestWrapper<T> wrapList(Collection<T> list, Class<T> clazz) {return new RestListWrapper<>(list, clazz, this, getTemplate(list, clazz));}

🔎 繼續下鉆查看RestListWrapper以及它的基類RestWrapperAdapter

    public void configurePersister(XStreamPersister persister, XStreamMessageConverter converter) {controller.configurePersister(persister, converter);}

從這里能看出來包裝器有個關于轉換器的配置的方法,而且類型是XStreamMessageConverter converter,繼續跟蹤代碼,查找下它是在哪里被調用的

這里看到有幾個繼承類,但是只有里面的類型和RestListWrapper是一樣的

public abstract class XStreamCatalogListConverterextends XStreamMessageConverter<RestListWrapper<?>> 

根據spring mvc的自動根據參數類型適配的原則,它用的轉換器就是XStreamCatalogListConverter,而且從注釋中也能看出來

/*** A wrapper for all Collection type responses using the {@link XStreamCatalogListConverter} (XML* and JSON output). Also supports Collection type responses using the {@link* FreemarkerHTMLMessageConverter}, but is not required for such responses.** <p>In the previous rest API this wasn't needed because in each individual rest request the* Collections were aliased to*/

在XStreamCatalogListConverter.java中能夠看到具體的轉換方法

protected void configureXStream(XStream xstream, Class<?> clazz, RestListWrapper<?> wrapper) {XStreamPersister xp = xpf.createXMLPersister();wrapper.configurePersister(xp, this);final String name = getItemName(xp, clazz);xstream.alias(name, clazz);xstream.registerConverter(new CollectionConverter(xstream.getMapper()) {@Overridepublic boolean canConvert(@SuppressWarnings("rawtypes") Class type) {return Collection.class.isAssignableFrom(type);}@Overrideprotected void writeCompleteItem(Object item,MarshallingContext context,HierarchicalStreamWriter writer) {writer.startNode(name);context.convertAnother(item);writer.endNode();}});xstream.registerConverter(new Converter() {@Overridepublic boolean canConvert(Class type) {return clazz.isAssignableFrom(type);}@Overridepublic void marshal(Object source,HierarchicalStreamWriter writer,MarshallingContext context) {String ref;// Special case for layer list, to handle the non-workspace-specific// endpoint for layersif (clazz.equals(LayerInfo.class)&& OwsUtils.getter(clazz, "prefixedName", String.class) != null&& RequestInfo.get() != null&& !RequestInfo.get().getPagePath().contains("/workspaces/")) {ref = (String) OwsUtils.get(source, "prefixedName");} else if (OwsUtils.getter(clazz, "name", String.class) != null) {ref = (String) OwsUtils.get(source, "name");} else if (OwsUtils.getter(clazz, "id", String.class) != null) {ref = (String) OwsUtils.get(source, "id");} else if (OwsUtils.getter(clazz, "id", Long.class) != null) {// For some reason Importer objects have Long ids so this catches that// caseref = OwsUtils.get(source, "id").toString();} else {throw new RuntimeException("Could not determine identifier for: " + clazz.getName());}writer.startNode(wrapper.getItemAttributeName());writer.setValue(ref);writer.endNode();encodeLink(encode(ref), writer);}@Overridepublic Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {return null;}});}

我修改了上述代碼中的

writer.startNode(wrapper.getItemAttributeName() +"test");

然后再次請求接口就能看到修改后的數據

寫在最后,文章難免有寫的不對或者不完善的地方,歡迎提出糾正意見

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/35017.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/35017.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/35017.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

zabbix-agent2啟動失敗報錯Unit zabbix-agent2.service entered failed state.

文章目錄 1&#xff0c;用systemctl status zabbix-agent2查看報錯狀態2&#xff0c;用journalctl -xe查看一下報錯日志3&#xff0c;再看一下zabbix的日志。4&#xff0c;錯誤修改5&#xff0c; 再次重啟zabbix-agent2 1&#xff0c;用systemctl status zabbix-agent2查看報錯…

高考季-計算機相關專業與所學核心課程介紹以及高考報考學校推薦

計算機相關專業通常包括計算機科學與技術、軟件工程、信息安全、網絡工程、人工智能等。以下是對這些專業的詳細介紹、所學課程內容以及一些推薦的學校和專業。 1. 計算機科學與技術 專業介紹 計算機科學與技術是研究計算機系統及其相關技術的學科&#xff0c;包括計算機硬件…

ref與reactive

在Vue 3中&#xff0c;ref 和 reactive 是兩種用于創建響應式狀態的API。它們在實現方式和使用場景上有所不同。下面是對 ref 和 reactive 的深度解析&#xff1a; 1. ref 解析 ref 用于創建一個單一的響應式引用&#xff0c;可以用來包裹基本類型&#xff08;如字符串、數字…

Word如何在頁眉中插入和刪除橫線

你平常是否遇見到Word的頁眉中有一條橫線&#xff0c;怎么也刪不了&#xff01;&#xff01;&#xff01; 今天劉小生分享如何在頁眉中插入和刪除橫線&#xff0c;我們一起操練起來吧&#xff01; 1、Word頁眉插入橫線 選擇【插入】-【頁眉頁腳】&#xff0c;在“頁眉頁腳”…

00_Python核心編程

Python入門 一 Python初識 1 Python的歷史 Python的歷史python是蟒蛇的含義python是一種解釋型的,面向對象的,帶有動態語義的高級程序設計語言. python是一種使你在編程時能夠保持自己的風格的程序設計語言,你不用費什么勁就可以實現你想要的功能,并且編寫的程序清晰易懂. …

ArcGIS Pro SDK (五)內容 5 元數據

ArcGIS Pro SDK &#xff08;五&#xff09;內容 5 收藏夾 目錄 ArcGIS Pro SDK &#xff08;五&#xff09;內容 5 收藏夾1 獲取其 IMetadata 接口2 獲取項目的元數據&#xff1a;獲取XML3 設置項目的元數據&#xff1a;設置XML項4 檢查元數據是否可以編輯&#xff1a;可以編輯…

可靠性評估的概念和流程

可靠性評估的概念和流程 可靠性評估是系統工程中的一項重要任務&#xff0c;它旨在確定系統的可靠性和預期的運行時間&#xff0c;以便進行設計優化和維護決策。其概念和流程通常涉及以下幾個關鍵要素&#xff1a; 可靠性模型&#xff1a; 可靠性模型是描述系統或組件性能的…

常見的排序算法【總結】

目錄 排序的基本概念與分類排序的穩定性內排序與外排序簡單排序冒泡排序時間復雜度&#xff1a; O ( n 2 ) O(n^2) O(n2) 簡單選擇排序排序原理&#xff1a;時間復雜度&#xff1a; O ( n 2 ) O(n^2) O(n2) 插入排序排序原理&#xff1a;時間復雜度&#xff1a; O ( n 2 ) O(n^…

晶方科技:臺積電吃飽,封裝迎春?

半導體產業鏈掀起漲價潮&#xff0c;先進封裝迎接利好。 這里我們來聊國內先進封裝企業——晶方科技。 近期&#xff0c;由于產能供不應求&#xff0c;臺積電決定上調先進封裝產品價格&#xff0c;還表示訂單已經排到2026年。 大哥吃不下了&#xff0c;剩下的訂單全都是空間。…

主線程和子線程

主線程 當Java程序啟動時&#xff0c;一個線程會立刻運行&#xff0c;該線程通常叫做程序的主線程&#xff08;main thread&#xff09;&#xff0c;即main方法對應的線程&#xff0c;它是程序開始時就執行的。 Java應用程序會有一個main方法&#xff0c;是作為某個類的方法出…

JDK 23:Loom改進版發布

1.新版 Loom EA 改進虛擬線程中的監視器&#xff08;同步方法&#xff09; Project Loom 發布了新的搶先體驗版本(23-loom4-102 - 2024/5/31)。改進了對象監視器實現&#xff0c;可以防止虛擬線程在以下情況下固定其載體線程&#xff1a; 當進入同步方法/語句時發生阻塞&…

問題-python-爬蟲無法爬取外網資源問題(python爬蟲)

方法一&#xff1a; 這個報錯通過關掉梯子就能解決&#xff0c;目前不清楚具體原理。 后續了解具體原理了&#xff0c;我會在這篇文章上更新具體分析—— 方法二&#xff1a; 也可以把這個東西打開&#xff0c;但是用完建議關掉。

python無法安裝scipy怎么辦

python安裝scipy時出現以下錯誤&#xff1a; from scipy.misc import imread Traceback (most recent call last):File "D:/Pyproject/qq_Spider/create_cloud.py", line 14, in <module>from scipy.misc import imread ModuleNotFoundError: No module named …

淺析Kubernetes的權限控制模型

Kubernetes是一個開源的容器編排引擎&#xff0c;用來對容器化應用進行自動化部署、擴縮和管理。它是一個強大的集群管理系統&#xff0c;提供了豐富的功能。他的一個核心組件是Kubernetes API Server&#xff0c;這是集群中所有資源管理的入口點&#xff0c;提供了一組RESTful…

spring boot jar 啟動報錯 Zip64 archives are not supported

spring boot jar 啟動報錯 Zip64 archives are not supported 原因、解決方案問題為什么 spring boot 不支持 zip64zip、zip64 功能上的區別zip 的文件格式spring-boot-loader 是如何判斷是否是 zip64 的&#xff1f; 參考 spring boot 版本是 2.1.8.RELEASE&#xff0c;引入以…

北京崇文門中醫醫院賈英才主任:腦梗治療新探索

腦梗&#xff0c;是眾多患者心中的陰霾&#xff0c;它的突然來襲&#xff0c;常常讓人猝不及防。 一旦發作&#xff0c;偏癱、失語等癥狀接踵而至&#xff0c;給患者及其家庭帶來沉重的打擊&#xff0c;極大地影響了生活的質量。 造成腦梗頻發的原因究竟是什么&#xff1f;中…

Golang | Leetcode Golang題解之第173題二叉搜索樹迭代器

題目&#xff1a; 題解&#xff1a; type BSTIterator struct {stack []*TreeNodecur *TreeNode }func Constructor(root *TreeNode) BSTIterator {return BSTIterator{cur: root} }func (it *BSTIterator) Next() int {for node : it.cur; node ! nil; node node.Left {it…

Docker部署前端,動態配置后端地址

本文介紹了使用Docker環境變量動態配置nginx。采用的是通過docker run -e xxxxxxx先往容器注入環境變量&#xff0c;然后進一步通過envsubst指令將環境變量寫入到conf文件中&#xff0c;實現動態配置文件內容。 背景 前后端分離的架構下&#xff0c;經常會用到nginx反向代理來…

粉末冶金5G智能工廠工業物聯數字孿生平臺,推進制造業數字化轉型

粉末冶金5G智能工廠工業物聯數字孿生平臺&#xff0c;推進制造業數字化轉型。在數字化浪潮席卷全球的今天&#xff0c;制造業的數字化轉型已然成為不可逆轉的趨勢。粉末冶金行業&#xff0c;作為制造業的重要一環&#xff0c;亦需緊跟時代步伐&#xff0c;以5G智能工廠、工業物…

【SpringSecurity】認證與鑒權框架SpringSecurity——授權

目錄 權限系統的必要性常見的權限管理框架SpringSecurity授權基本流程準備腳本限制訪問資源所需權限菜單實體類和Mapper封裝權限信息封裝認證/鑒權失敗處理認證失敗封裝鑒權失敗封裝配置SpringSecurity 過濾器跨域處理接口添加鑒權hasAuthority/hasAnyAuthorityhasRole/? hasA…