Spring?Boot?項(xiàng)目在?K8S?中的打包、部署與運(yùn)維發(fā)布實(shí)踐指南
面向運(yùn)維工程師,從基礎(chǔ)概念到流水線發(fā)布、再到上線保障與故障排查,逐步建立對(duì)
Spring Boot + Docker + K8S + JVM的完整認(rèn)知。
一、為什么運(yùn)維要掌握 Spring Boot 項(xiàng)目的發(fā)布鏈路
1. 本文目標(biāo)
- 說(shuō)明運(yùn)維為什么不能只會(huì)
kubectl apply - 建立從源碼到上線的完整交付視角
- 幫助運(yùn)維理解
Spring Boot項(xiàng)目為什么能以jar形式運(yùn)行 - 幫助運(yùn)維提升
K8S流水線發(fā)布成功率
2. 適合誰(shuí)看
- 正在接觸 Java 項(xiàng)目交付的運(yùn)維工程師
- 想搞懂
jar、war、Tomcat、Maven關(guān)系的人 - 想系統(tǒng)學(xué)習(xí)
Spring Boot在K8S中部署流程的人
3. 運(yùn)維最容易踩的坑
- 只會(huì)改鏡像 tag,不理解交付物類型
- 不清楚
jar和war的運(yùn)行環(huán)境差異 - 不理解
JVM與容器內(nèi)存限制的關(guān)系 - 不會(huì)判斷到底是構(gòu)建失敗、鏡像失敗,還是
K8S啟動(dòng)失敗
什么是 Java 項(xiàng)目的“交付物”
- 什么是源碼:
- 源碼就是源代碼。
- 什么是編譯產(chǎn)物:
- 編譯產(chǎn)物就是源碼經(jīng)過(guò)編譯處理之后的交付結(jié)果,比如 Java 的源碼經(jīng)過(guò) Maven 處理后,會(huì)生成
.class文件、jar包、war包。
- 什么是
jarjar是Java Archive,是 Java 的一種打包格式,本質(zhì)上是基于 zip 格式的壓縮包,專門(mén)用于打包 Java 類文件、資源文件和元數(shù)據(jù)。- 里面會(huì)放置編譯后的一些文件:編譯后的
.class文件、依賴包、配置文件、資源文件。 jar包主要分為普通jar包和可執(zhí)行jar包。- 普通
jar包僅包含編譯后的.class文件和資源,無(wú)外部依賴,不能直接運(yùn)行;而可運(yùn)行jar中通常會(huì)把依賴和內(nèi)嵌的 Web 容器一起打包進(jìn)去,不需要外置 Tomcat,可以通過(guò)java -jar直接執(zhí)行。Spring Boot 應(yīng)用就屬于這種一鍵部署方式。
- 什么是
warwar包是Web Application Archive,是 Java Web 應(yīng)用的打包格式,同樣基于 zip 壓縮,專門(mén)用于打包 Web 應(yīng)用資源(servlet、jsp、靜態(tài)資源等)。與jar相比,war僅適用于 Java Web 應(yīng)用,結(jié)構(gòu)固定(需包含WEB-INF/、WEB-INF/classes/、WEB-INF/lib/等),必須部署到 Servlet 容器(如 Tomcat)中,由容器負(fù)責(zé)加載、初始化和調(diào)度。- 傳統(tǒng)的 Java Web 應(yīng)用(如 Spring MVC)常使用這種方式。
jar 包的典型架構(gòu):
myapp.jar ├── META-INF/ │ └── MANIFEST.MF # 包含Main-Class和Class-Path配置 ├── BOOT-INF/ │ ├── classes/ # 業(yè)務(wù)代碼.class文件 │ └── lib/ # 所有依賴Jar包 └── org/springframework/boot/loader/ # Spring Boot啟動(dòng)器
war 包典型架構(gòu):
myapp.war ├── WEB-INF/ │ ├── web.xml # Web應(yīng)用配置文件(傳統(tǒng)項(xiàng)目必需) │ ├── classes/ # 業(yè)務(wù)代碼.class文件 │ └── lib/ # Web應(yīng)用依賴Jar包 ├── META-INF/ │ └── MANIFEST.MF ├── index.jsp # JSP頁(yè)面 ├── static/ # 靜態(tài)資源(CSS、JS、圖片) └── templates/ # 模板文件(如Thymeleaf、FreeMarker)
什么是Spring Boot
Spring Boot解決了什么問(wèn)題- springboot 的核心目標(biāo)主要是為了簡(jiǎn)化 spring 應(yīng)用的初始化搭建和開(kāi)發(fā)過(guò)程。主要解決傳統(tǒng) spring 開(kāi)發(fā)配置繁瑣、依賴管理復(fù)雜、部署麻煩、監(jiān)控缺失等問(wèn)題,讓開(kāi)發(fā)者能夠更快上手,專注于業(yè)務(wù)邏輯開(kāi)發(fā)。
- 為什么它常見(jiàn)產(chǎn)物是可執(zhí)行
jar - 主要是因?yàn)?Spring Boot 構(gòu)建出的
jar包能夠獨(dú)立運(yùn)行、簡(jiǎn)化部署,符合微服務(wù)架構(gòu)需求,保證服務(wù)獨(dú)立部署運(yùn)行,不需要外部環(huán)境依賴。適合通過(guò) Docker 等容器化方式進(jìn)行啟動(dòng)。運(yùn)維側(cè)也更加方便,更新回滾時(shí)僅替換文件即可。 - 內(nèi)嵌
Tomcat的基本原理 - springboot 內(nèi)嵌 tomcat 的核心是將 tomcat 作為普通的 Java 對(duì)象運(yùn)行在應(yīng)用中,而非獨(dú)立的外部進(jìn)程。
- 當(dāng)引入
spring-boot-starter-web時(shí),會(huì)自動(dòng)傳遞引入 Tomcat 內(nèi)嵌依賴: tomcat-embed-core是 Tomcat 核心引擎,tomcat-embed-el是表達(dá)式語(yǔ)言支持,tomcat-embed-websocket是 WebSocket 支持(可選)。
Spring Boot 通過(guò)自動(dòng)配置機(jī)制完成 Tomcat 的初始化和啟動(dòng)。
4. 什么是Maven
maven 是 Java 項(xiàng)目的自動(dòng)化構(gòu)建和依賴管理工具。
你可以把它理解成 Java 項(xiàng)目的管家:自動(dòng)幫你下載所有 jar 包,自動(dòng)幫你編譯、打包、運(yùn)行、測(cè)試,統(tǒng)一管理項(xiàng)目結(jié)構(gòu)、版本、依賴。
一句話:不用手動(dòng)找 jar、手動(dòng)導(dǎo)包、手動(dòng)配置,Maven 全部自動(dòng)化搞定。pom.xml 是 Maven 項(xiàng)目的核心配置文件,使用哪些依賴、如何打包、使用什么插件,都在這里定義。
二、Spring Boot 項(xiàng)目的打包發(fā)布
springboot 項(xiàng)目在前面已經(jīng)介紹過(guò)了,通過(guò) Java 就可以啟動(dòng)構(gòu)建好的 jar 包。
通常會(huì)先通過(guò) Docker 鏡像進(jìn)行打包,然后再到集群中部署。
下面詳細(xì)介紹一下 Spring Boot 項(xiàng)目的打包構(gòu)建流程。
三、Jenkins 流水線發(fā)布流程
現(xiàn)在使用的流水線打包配置,基本都通過(guò) Jenkins 來(lái)實(shí)現(xiàn),而 Jenkins 中的打包過(guò)程主要有以下幾步:
在當(dāng)前的流水線平臺(tái)架構(gòu)上,通過(guò)定義好的 Jenkinsfile 來(lái)指導(dǎo) Jenkins 的構(gòu)建流程。這里就構(gòu)建、打包以及發(fā)布的過(guò)程進(jìn)行梳理:
- Jenkins 拉取配置倉(cāng),將提前定義好的部署文件、
Dockerfile等文件放到工作目錄。 - Jenkins 根據(jù)填寫(xiě)的倉(cāng)庫(kù)地址拉取項(xiàng)目源碼。
- Jenkins 中進(jìn)行編譯檢查,使用
mvn complie。 - 觸發(fā) Docker 構(gòu)建。Docker 構(gòu)建一般可以分為兩種:先構(gòu)建打包鏡像產(chǎn)物,再構(gòu)建運(yùn)行鏡像;或者全部放到 Docker 構(gòu)建中做多階段構(gòu)建。
兩種方式各有優(yōu)劣。為了更好地管理流水線、減少磁盤(pán)空間占用,線上采用第二種方式,將打包和構(gòu)建都放到一個(gè) Dockerfile 中執(zhí)行。
示例 Dockerfile:
FROM maven:3.3.9 as BUILD
#構(gòu)建構(gòu)建鏡像且命名為build
COPY . /usr/app/
RUN cd /usr/app; mvn clean package -Dmaven.test.skip=true
## 運(yùn)行打包命令
FROM your-jdk-runtime:8
#導(dǎo)入運(yùn)行鏡像
COPY --from=BUILD /usr/app/your-app/target/your-app.jar /usr/local/apps/
## 關(guān)鍵,將從前一個(gè)名為build的構(gòu)建階段容器的文件系統(tǒng)里面,把文件復(fù)制到當(dāng)前這個(gè)運(yùn)行階段的鏡像里面。,這就是將構(gòu)建和運(yùn)行分開(kāi)。
ENV APP_BASE /usr/local/apps/
WORKDIR /usr/local/apps/
RUN ping -c 4 gitlab.example.com && \
yum install -y git && mkdir -p /tmp/gitfile && \
cd /tmp/gitfile && git init && \
git remote add origin -f gitlab.example.com/example-group/shell.git && \
echo springboot/start.sh >> .git/info/sparse-checkout && \
git config core.sparsecheckout true && \
git pull origin master && chmod +x /tmp/gitfile/springboot/start.sh && \
mv /tmp/gitfile/springboot/start.sh /usr/local/apps/
## 這里是做一些見(jiàn)檢查,然后拉取一個(gè)啟動(dòng)腳本,啟動(dòng)腳本中定義的一些環(huán)境變量的相關(guān)信息。
EXPOSE 8080
## springboot的主業(yè)務(wù)端口
EXPOSE 10090
## 管理端口
CMD ["./start.sh", "your-app.jar"]
#通過(guò)腳本去啟動(dòng)jar包從上面的 Dockerfile 中可以看到,Jenkins 沒(méi)有在宿主機(jī)上直接執(zhí)行 mvn 打包命令。Jenkins 負(fù)責(zé)觸發(fā) docker build,而實(shí)際的打包構(gòu)建是在 docker build 階段執(zhí)行的 mvn clean package,也就是 jar 包是在容器構(gòu)建中生成的。
可以看出,在打包構(gòu)建階段引用的是 maven:3.3.9,然后執(zhí)行的打包命令是 mvn clean package -Dmaven.test.skip=true。
打包完成之后,再把 jar 拷貝到運(yùn)行鏡像目錄中,構(gòu)建出最終運(yùn)行鏡像。
- 在運(yùn)行鏡像構(gòu)建完成之后,Jenkins 會(huì)生成一個(gè)帶時(shí)間戳、
commit id、隨機(jī)串的 tag,然后推送到 Harbor,再進(jìn)入部署階段。 - 部署階段,Jenkins 通過(guò)選擇指定的 K8S 環(huán)境,調(diào)用
deployment.yaml,替換 YAML 中的鏡像名為構(gòu)建完成的運(yùn)行鏡像,然后執(zhí)行發(fā)布操作。
四、流水線環(huán)節(jié)中容易失敗的地方
Maven依賴下載失?。ㄋ椒?Nexus/Artifactory不可用、認(rèn)證過(guò)期、倉(cāng)庫(kù)地址變更、網(wǎng)絡(luò)超時(shí))- 源碼拉取失敗(
Git憑證失效、分支/tag 不存在、子模塊或大倉(cāng)超時(shí)) Maven編譯/測(cè)試失?。ùa沖突、本地與流水線pom不一致、跳測(cè)參數(shù)與質(zhì)量門(mén)禁不匹配)- 構(gòu)建環(huán)境
JDK/Maven版本與本地或Dockerfile基鏡像不一致 Dockerfile多階段構(gòu)建失?。窂綄?xiě)錯(cuò)、COPY --from階段名錯(cuò)誤、構(gòu)建階段內(nèi)存不足)- 鏡像構(gòu)建失敗(基礎(chǔ)鏡像拉取失敗、
RUN命令非 0 退出、磁盤(pán)空間滿) - 鏡像推送失敗(
Harbor登錄過(guò)期、項(xiàng)目配額滿、網(wǎng)絡(luò)或 TLS 問(wèn)題) K8S拉鏡像失?。?code>imagePullSecrets 缺失、tag 未推上去、鏡像名與部署 YAML 不一致)Pod啟動(dòng)失?。?code>CrashLoopBackOff、配置/密鑰缺失、JVM堆大于容器內(nèi)存限制)- 探針失敗(
readiness/liveness路徑或端口與真實(shí)監(jiān)聽(tīng)不一致、初始延遲過(guò)短、依賴未就緒) - 發(fā)布階段失敗(
YAML語(yǔ)法錯(cuò)誤、資源配額不足、RBAC無(wú)權(quán)限、Deployment與HPA/PDB沖突)
五、運(yùn)維如何提升發(fā)布成功率
- 固化構(gòu)建基線:流水線與
Dockerfile使用固定版本的JDK、Maven、基礎(chǔ)鏡像;重大升級(jí)走單獨(dú)變更,避免「同一套流水線突然換版本」。 - 依賴與制品可復(fù)現(xiàn):私服高可用、憑證輪換有流程;必要時(shí)對(duì)關(guān)鍵依賴做緩存層或構(gòu)建節(jié)點(diǎn)本地
.m2緩存策略,減少外網(wǎng)抖動(dòng)影響。 - 鏡像與部署聯(lián)動(dòng):
tag規(guī)則統(tǒng)一(時(shí)間戳 +commit id);部署前校驗(yàn)鏡像在倉(cāng)庫(kù)中可拉??;imagePullPolicy與回滾策略和團(tuán)隊(duì)約定一致。 - 資源與 JVM 對(duì)齊:為容器設(shè)置合理
requests/limits,JVM-Xmx等明顯小于容器內(nèi)存上限,避免OOMKilled;大構(gòu)建任務(wù)單獨(dú)調(diào)高構(gòu)建 Pod/節(jié)點(diǎn)的內(nèi)存與超時(shí)。 - 探針與啟動(dòng)順序:與研發(fā)確認(rèn)健康檢查 URL、端口、依賴就緒時(shí)間;適當(dāng)調(diào)大
initialDelaySeconds,區(qū)分liveness與readiness語(yǔ)義,避免誤殺仍在啟動(dòng)的進(jìn)程。 - 配置與密鑰:
ConfigMap/Secret變更納入發(fā)布 checklist;避免「只改鏡像不改配置」導(dǎo)致啟動(dòng)即失??;密鑰輪換后同步更新K8S與流水線憑據(jù)。 - 可觀測(cè)與快速回滾:發(fā)布前后看構(gòu)建日志、事件
kubectl describe、Pod日志;保留上一版可用鏡像 tag,出問(wèn)題優(yōu)先rollout undo或改回舊 tag。 - 分環(huán)境與灰度:測(cè)試/預(yù)發(fā)與生產(chǎn)隔離;生產(chǎn)盡量金絲雀或分批發(fā)布,降低單次失敗影響面。
六、運(yùn)維必須掌握的 JVM 基礎(chǔ)與參數(shù)設(shè)置
1. 為什么運(yùn)維要懂JVM
- Java 服務(wù)性能和穩(wěn)定性直接受
JVM影響 - 容器內(nèi)存限制與
JVM堆配置強(qiáng)相關(guān) - 發(fā)布成功不代表運(yùn)行穩(wěn)定
java 服務(wù)的所有代碼都運(yùn)行在 JVM 上,JVM 的行為直接決定了服務(wù)的性能和穩(wěn)定性。所以 JVM 的配置很重要,關(guān)聯(lián)業(yè)務(wù)代碼是否能夠穩(wěn)定運(yùn)行。
運(yùn)維側(cè)必須掌握的 JVM 技能:
- 能夠看懂 GC 日志,識(shí)別 GC 頻繁、GC 停頓過(guò)長(zhǎng)。
- 會(huì)用
jstack抓取線程棧,定位死鎖、CPU 高的線程。 - 會(huì)用
jmap抓取堆 dump,分析內(nèi)存泄漏。 - 會(huì)配置基本的 JVM 參數(shù)(堆大小、GC 收集器)。
容器內(nèi)存限制與 JVM 堆配置強(qiáng)相關(guān)。
因?yàn)槿萜鞯囊?guī)格限制與 JVM 的限制相關(guān),JVM 配置不能大于容器規(guī)格限制。JVM 默認(rèn)運(yùn)行在容器里面,而集群部署對(duì)于容器規(guī)格是有限制的。
例如在集群中通過(guò) resources.limits.memory=2G 限制了容器占用內(nèi)存的大小,如果 JVM 占用大于 2G,那么容器會(huì)被識(shí)別為超出限制并直接重啟。這時(shí)候會(huì)觸發(fā) OOM,Pod 會(huì)被強(qiáng)制重啟。
而在 Pod 日志中,僅能看到退出原因?yàn)?OOMKilled。
在 Java 11+ 版本中,自帶容器感知能力,默認(rèn)使用容器內(nèi)存的 25% 作為堆大小。
所以在 JVM 示例中,通常需要顯式指定 JVM 大小。
例如:
env:
- name: JAVA_OPTS
value: "-XX:MaxRAMPercentage=75.0 -XX:InitialRAMPercentage=75.0"
resources:
limits:
memory: "2Gi"
常見(jiàn)問(wèn)題是服務(wù)部署發(fā)布完成后,運(yùn)行一段時(shí)間 Pod 又會(huì)被重啟,服務(wù)偶爾出現(xiàn)異常中斷。
JVM 的問(wèn)題大多是累積形式:1. 內(nèi)存泄漏;2. GC 問(wèn)題;3. 線程問(wèn)題;4. 大對(duì)象問(wèn)題等。
所以在發(fā)布后,不能只關(guān)注 Pod 是否 Running,還需要關(guān)注 Pod 的內(nèi)存使用情況、GC 次數(shù)等指標(biāo)。
介紹一下 GC:
GC 是垃圾回收機(jī)制,是 JVM 自帶的自動(dòng)內(nèi)存管理機(jī)制。
你可以把 JVM 堆內(nèi)存想象成一個(gè)倉(cāng)庫(kù):
- 你的 Java 代碼運(yùn)行時(shí),會(huì)不斷創(chuàng)建新對(duì)象(往倉(cāng)庫(kù)里放東西)
- 有些對(duì)象用完就沒(méi)用了(變成垃圾)
- GC 就是倉(cāng)庫(kù)的清潔工,自動(dòng)把沒(méi)用的垃圾清走,騰出空間
為什么 GC 會(huì)搞崩你的服務(wù):
因?yàn)?GC 在工作時(shí),會(huì)暫停所有業(yè)務(wù)線程。這個(gè)暫停就是 STW,是很多 Java 服務(wù)卡頓的根源。
GC 的類型有 Young GC 和 Full GC。其中 Young GC 頻繁問(wèn)題不大,只要不耗時(shí)太長(zhǎng);Full GC 是更危險(xiǎn)的信號(hào),只要 Full GC 超過(guò) 1 次 / 分鐘,或者單次超過(guò) 1 秒,服務(wù)通常就會(huì)出問(wèn)題。
而在容器 Pod 中,查看 GC 日志和 GC 指標(biāo),才能定位問(wèn)題。
方式 1:進(jìn)入 Pod 直接查看 GC 日志(前提是 GC 日志開(kāi)啟了打印和收集)。
常見(jiàn)的位置:
# 最常見(jiàn):和 app.jar 同目錄 ls -l /app/gc*.log # 有些項(xiàng)目會(huì)放在 logs 目錄 ls -l /app/logs/gc*.log # 找不到就全局搜 find / -name "gc*.log" 2>/dev/null
示例:
# Full GC 日志(重點(diǎn)看這行) 2024-05-20T10:30:00.123+08:00: [Full GC (System.gc()) 1500M->800M(2048M), 2.5s]
從這條記錄中可以看到,發(fā)生了 Full GC,發(fā)生前使用了 1500M,GC 后使用了 800M。
這次 GC 的總耗時(shí)時(shí)間是 2.5s。
抓取堆棧 dump 和線程棧:
當(dāng)發(fā)現(xiàn) Full GC 頻繁、內(nèi)存一直上漲時(shí),就要懷疑是內(nèi)存泄漏,此時(shí)要抓取堆 dump(內(nèi)存快照)來(lái)進(jìn)行分析。
抓取方式:
# 進(jìn)入 Pod kubectl exec -it <pod-name> -- /bin/bash # 找到 Java 進(jìn)程 ID(一般是 1,因?yàn)槿萜骼镏挥幸粋€(gè)進(jìn)程) jps # 抓取堆 dump(會(huì)生成一個(gè) hprof 文件) jmap -dump:format=b,file=/app/heapdump.hprof 1 # 從 Pod 復(fù)制到本地 kubectl cp <pod-name>:/app/heapdump.hprof ./heapdump.hprof
使用工具分析 dump 文件:
Eclipse MAT:最常用的內(nèi)存分析工具JProfiler:功能更強(qiáng)大的商業(yè)工具
Pod 內(nèi)存使用率高不等于一定有問(wèn)題。
舉個(gè)例子:
- 你給 Pod 限制了 2G 內(nèi)存
- JVM 堆配置了 1.5G
- 運(yùn)行一段時(shí)間后,Pod 內(nèi)存使用率到了 80%(1.6G)
這完全正常,因?yàn)?JVM 會(huì)把內(nèi)存用滿,然后觸發(fā) GC 回收。只要 GC 能回收,內(nèi)存使用率就會(huì)降下來(lái)。
真正有問(wèn)題的是:
- GC 后內(nèi)存使用率還是 80% 以上
- Full GC 越來(lái)越頻繁
- 內(nèi)存使用率一直漲,直到 OOM
運(yùn)維排查 GC 問(wèn)題標(biāo)準(zhǔn)流程:
- 發(fā)現(xiàn)問(wèn)題:Grafana 告警
Full GC頻繁、接口超時(shí)或 Pod 內(nèi)存使用率高。 - 初步判斷:
kubectl logs <pod-name> | grep "Full GC",看 Full GC 次數(shù)和耗時(shí)。 - 深入分析:進(jìn)入 Pod 看完整 GC 日志,看 GC 前后內(nèi)存變化。
- 抓取證據(jù):如果懷疑內(nèi)存泄漏,抓取堆 dump。
- 臨時(shí)解決:重啟 Pod(能暫時(shí)緩解,但不能根治)。
- 根治問(wèn)題:分析 dump 文件,找到泄漏點(diǎn),讓開(kāi)發(fā)修復(fù)。
到此這篇關(guān)于Spring Boot 項(xiàng)目在 K8S 中的打包、部署與運(yùn)維發(fā)布實(shí)踐指南的文章就介紹到這了,更多相關(guān)Spring Boot K8S 打包、部署與運(yùn)維發(fā)布內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot+Docker+K8s云原生部署全流程(從零到發(fā)布)
- K8S(Docker)如何優(yōu)雅的關(guān)閉SpringBoot微服務(wù)
- k8s部署springboot實(shí)現(xiàn)前后端分離項(xiàng)目
- k8s+springboot+CronJob定時(shí)任務(wù)部署實(shí)現(xiàn)
- 手把手教你k8s部署springboot服務(wù)
- springboot項(xiàng)目部署到k8s上的方法步驟
- 阿里云k8s服務(wù)springboot項(xiàng)目應(yīng)用升級(jí)時(shí)出現(xiàn)502錯(cuò)誤
- 使用Stargate訪問(wèn)K8ssandra的過(guò)程之Springboot整合Cassandra
- SpringBoot應(yīng)用快速部署到K8S的詳細(xì)教程
相關(guān)文章
快速解決SpringMVC @RequestBody 用map接收請(qǐng)求參數(shù)的問(wèn)題
今天小編就為大家分享快速解決SpringMVC @RequestBody 用map接收請(qǐng)求參數(shù)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
springboot加載命令行參數(shù)ApplicationArguments的實(shí)現(xiàn)
本文主要介紹了springboot加載命令行參數(shù)ApplicationArguments的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
【java 多線程】守護(hù)線程與非守護(hù)線程的詳解
這篇文章主要介紹了java守護(hù)線程與非守護(hù)線程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
Java?并發(fā)編程基礎(chǔ)概念與常見(jiàn)問(wèn)題整理
這篇文章主要介紹了Java?并發(fā)編程基礎(chǔ)概念與常見(jiàn)問(wèn)題整理,本文將帶你走進(jìn)Java并發(fā)編程的世界,系統(tǒng)梳理基礎(chǔ)概念、剖析常見(jiàn)問(wèn)題,并補(bǔ)充實(shí)用細(xì)節(jié),為后續(xù)深入學(xué)習(xí)打下堅(jiān)實(shí)基礎(chǔ),感興趣的朋友跟隨小編一起看看吧2026-03-03
SpringBoot搭建Dubbo項(xiàng)目實(shí)現(xiàn)斐波那契第n項(xiàng)詳解
這篇文章主要講解了“SpringBoot+Dubbo怎么實(shí)現(xiàn)斐波那契第N項(xiàng)”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)吧2022-06-06
Java中的CopyOnWriteArrayList深入解讀
這篇文章主要介紹了Java中的CopyOnWriteArrayList深入解讀,在 ArrayList 的類注釋上,JDK 就提醒了我們,如果要把 ArrayList 作為共享變量的話,是線程不安全的,需要的朋友可以參考下2023-12-12
Spring MVC實(shí)現(xiàn)一次簡(jiǎn)單的CRUD示例
這篇文章主要介紹了Spring MVC實(shí)現(xiàn)一次簡(jiǎn)單的CRUD示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
Mybatis 實(shí)現(xiàn)動(dòng)態(tài)組裝查詢條件,仿SQL模式
這篇文章主要介紹了Mybatis 實(shí)現(xiàn)動(dòng)態(tài)組裝查詢條件,仿SQL模式的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
mybatis-plus樂(lè)觀鎖實(shí)現(xiàn)方式詳解
這篇文章主要介紹了mybatis-plus樂(lè)觀鎖實(shí)現(xiàn)方式,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01

