從使用傳統Web框架到切換到Spring Boot後的總結
1、前言
其實我接觸 Spring Boot 的時間並不長,所以還算一個初學者,這篇文章也算是我對 Spring Boot 學習以及使用過程中的覆盤,如果文章出現描述錯誤或表達不清晰的地方,歡迎大家在評論區留言互動。
沒想到 Spring Boot 這兩年竟然普及的這麼快, 兩年前剛畢業的時候,因為待的是二線城市的小公司,公司的技術棧並不追求新穎,而是追求穩定、能用就行的理念,所以專案上就一直使用傳統的 SSM、SSH。
當搭建後端框架時,需要手動新增 Maven 配置、各種 XML 配置,反正就是一個專案建立就要配置一遍,從我兩年前寫的這篇 SSM框架搭建,就可以看出該過程是有多麼的繁瑣,甚至有時候因為配置錯誤以致於一上午就又可以愉快的划水了…
而專案部署時也是頭大,首先需要安裝 Tomcat,然後將專案編譯打包成 war 包,再將 war 包放在 Tomcat 的 webapp 目錄下部署執行,這個過程就覺得很不方便…
彙總一下構建一個傳統專案需要的步驟:
- 配置 web.xml,載入 Spring 和 Spring MVC
- 配置資料庫連線、配置 Spring 事務
- 配置載入配置檔案的讀取,開啟註解
- 配置日誌檔案
- Redis、MQ 等等 …
- 配置完成之後部署 Tomcat 除錯
- 可能還需要考慮各個版本的相容性,jar 包衝突的各種可行性。
因為如上種種問題,當一接觸到 Spring Boot 後就被它簡單的操作吸引了…
真羨慕現在的小夥伴,一上來就是用的 Spring Boot 了~ 不用再像我那會一樣。
2、Spring Boot 解決的問題
Spring Boot 的出現大大簡化了傳統 Web 框架的搭建過程,提高了開發效率,Spring Boot 總結後有以下幾個特點:
- 可以快速的建立 Spring 應用。
- 使用嵌入式的Servlet容器,應用無需打成WAR包
- 整合了大量第三方框架,做到開箱即用。
- 約定大於配置。
這幾個特點基本上也是面試 Spring Boot 時常問的,也正是因為這幾個特點讓之前繁瑣的搭建過程變的簡單。
我們都知道,特點並不是解決問題的關鍵,所以我們要了解,Spring Boot 到底是如何解決問題的。
2.1、自動配置
之前我們使用 XML 方式時,有相當大的部分就是對 Bean 進行初始化並配置,比如下面這一段就是配置資料庫連線資訊:
<!-- 配置 資料來源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://10.211.55.4:3306/test" />
<property name="username" value="root" />
<property name="password" value="123456" />
</bean>
而使用 Spring Boot 後,會根據某些約定的規則對所有配置的 Bean 進行初始化,也就是約定優於配置,然後有的小夥伴就會問,那麼什麼是約定優於配置呢?
約定優於配置可以這樣理解:
- 開發人員僅需要規定應用中不符合約定的部分。
- 在沒有規定配置的地方,採用預設配置
舉例說明:
不符合規定的部分:例如,如果模型中有個名為 User 的類,那麼資料庫中對應的表就會預設命名為 user。只有在偏離這一約定時,例如將該表命名為 “user_info”,才需寫有關這個名字的配置。
規定預設配置的地方:
- Maven 的目錄結構。專案建立後預設有 resources 資料夾,用於存放資源配置檔案。預設的編譯生成的類都在targe資料夾下面。
- Spring Boot預設的配置檔案必須是,也只能是字尾名為 yml 或者 properties 的檔案,且名稱唯一。
- application.yml中預設的屬性。資料庫連線資訊必須是以 spring: datasource: 為字首;再就是其他環境配置,比如埠號、請求路徑等,後面單獨寫一篇文章關於預設配置檔案的。
是否對這個 yml 或者 properties 檔案中配置了資訊就實現了配置有點好奇?
我們還是以資料庫連線資訊為例:在 Spring Boot 中有一個 DataSourceAutoConfiguration 配置類,這個類會自動查詢 application.yml 或者 properties 檔案裡的 spring.datasource.* 路徑,然後相關屬性會自動配置到資料來源中。這個過程就屬於約定大於配置,如果感興趣的小夥伴可以進入這個類看看。
2.2、內嵌容器
Spring Boot 應用程式可以不用部署到外部容器中,比如 Tomcat。Spring Boot 應用可以直接通過 Maven 命令將專案編譯成可執行的 jar 包,通過 java -jar 命令啟動即可,非常方便。
怎麼不需要 Servlet 容器就啟動起來了呢?
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
只要加上這個依賴,Spring Boot 就幫我們預設內嵌了 Tomcat,當然同樣也支援修改,比如可以使用 jetty(見標籤7修改預設內建的Tomcat容器)。
內嵌就內嵌了吧,簡單看一下 Tomcat 在 Spring Boot 中是如何啟動的。
可以看一下我寫的這篇文章:https://www.cnblogs.com/niceyoo/p/14019428.html
2.3、應用監控
應用監控是專案部署中必不可少的環節,以前我們怎麼知道系統實際執行的情況呢?
需要我們人為的進行運維監控,比如對 cpu、記憶體、資料庫連線等監控。如果是對系統介面,那麼可能會單獨寫個測試介面來判斷當前應用的健康程度,當然,大部分情況介面只要返回200就認為當前應用是健康的。
但是這種情況會存在很大的問題,首先前者會浪費人力資源,後者則因為固定介面形式無法真正意義上判斷應用的健康狀態。
而 Spring Boot 中提供了一個用於監控和管理自身應用資訊的模組—Actuator,通過 Actuator,可以實現對程式內部執行情況進行監控,比如 Bean 載入情況、環境變數、日誌資訊、執行緒資訊等。當然也可以自定義跟業務相關的監控,通過 Actuator 的端點資訊進行暴露。這個有點 DevOps 的意思。
如何整合到 Spring Boot 中呢?
只需要在 pom.xml 中新增如下依賴即可:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
應用啟動則直接可以訪問:
- http://localhost:port/actuator 檢視所有端點資訊。
- http://localhost:port/actuator/env 檢視該應用全部環境屬性。
application配置檔案中可配置的引數:
management:
endpoints:
web:
# actuator的訪問路徑,替換預設/actuator
base-path: /monitor
# 設定是否暴露端點 預設只有health和info可見
exposure:
# include: env # 方式1: 暴露端點env,配置多個以,隔開
include: "*" # 方式2: 包括所有端點,注意需要新增引號
# 排除端點,如果不排除則只需要訪問該應用的/shutdown 端點就能實現關閉該應用的遠端操作
exclude: shutdown
server:
port: 8888 #新開監控埠,不和應用用同一個埠,
endpoint:
health:
show-details: always # 顯示db、redis、rabbti連線情況等
shutdown:
enabled: true #預設情況下,除shutdown以外的所有端點均已啟用。手動開啟
按照如上配置則訪問路徑為:http://localhost:8888/monitor
我們可以看到有很多節點(圖中省略),這些結點被稱作 端點,如下是這些端點的描述:
ID | 描述 | 預設啟用 | 預設公開 |
---|---|---|---|
auditevents | 公開當前應用程式的審計事件資訊 | Yes | No |
beans | 顯示應用程式中所有Spring bean的完整列表 | Yes | No |
conditions | 顯示在配置和自動配置類上評估的條件以及它們是否匹配的原因 | Yes | No |
configprops | 顯示所有@ConfigurationProperties 對照的列表 | Yes | No |
env | 從Spring的ConfigurableEnvironment 中公開屬性 | Yes | No |
flyway | 顯示已應用的任何Flyway資料庫遷移 | Yes | No |
health | 顯示應用程式健康資訊 | Yes | Yes |
httptrace | 顯示HTTP跟蹤資訊(預設情況下,最後100個HTTP請求-響應互動) | Yes | No |
info | 顯示任意應用程式資訊 | Yes | Yes |
loggers | 顯示和修改應用程式中記錄器的配置 | Yes | No |
liquibase | 顯示已應用的任何Liquibase資料庫遷移 | Yes | No |
metrics | 顯示當前應用程式的“指標”資訊 | Yes | No |
mappings | 顯示所有@RequestMapping 路徑對照的列表 | Yes | No |
scheduledtasks | 顯示應用程式中排程的任務 | Yes | No |
sessions | 允許從Spring Session支援的會話儲存中檢索和刪除使用者會話 | Yes | No |
shutdown | 讓應用程式優雅地關閉 | No | No |
threaddump | 執行執行緒轉儲 | Yes | No |
如上所知,如果想顯示應用程式健康資訊,那麼就訪問 health 端點,即:
http://127.0.0.1:8888/monitor/health
其他的大家可以自行嘗試看一下,額外需要注意的是 shutdown 端點,專案中一定要排除,否則只需要訪問該應用的 /shutdown 端點就能實現該應用的遠端關閉操作,十分危險。
這就完了?
顯然不是,上邊這樣直接訪問端點,然後返回 JSON 資料,顯然很不直觀,畢竟現在很流行視覺化嘛~ 咳咳。
所以這時候我們可以通過 Spring Boot Admin 來對 actuator 返回的這些資料進行整理。
關於 Spring Boot Admin 的介紹:
Spring Boot Admin 是用於管理和監控 Spring Boot 應用程式執行狀態的。在 Spring Boot 專案中可以通過整合 Spring Boot Admin Client 向 Spring Boot Admin Server 進行註冊(通過 HTTP),這樣我們就可以在 Spring Boot Admin Server 統一管理 Spring Boot 應用。視覺化是端點之上的 Vue 專案。
提供功能如下:
- 顯示健康狀態及詳細資訊,如JVM和記憶體指標、資料來源指標、快取指標
- 跟蹤並下載日誌檔案
- 檢視jvm系統-和環境屬性
- 檢視Spring啟動配置屬性
- 方便loglevel管理
- 檢視執行緒轉儲
- 檢視http-traces
- 檢視http端點
- 檢視計劃任務
- 檢視和刪除活動會話(使用spring-session)
- 狀態更改通知(通過電子郵件、Slack、Hipchat…)
- 狀態變化的事件日誌(非永續性)
- ……
使用 Spring Boot Admin 也是非常的簡單,直接新增如下依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.3.0</version>
</dependency>
配置檔案如下:
spring:
boot:
admin:
# 修改上下文路徑
context-path: /admin
client:
url: http://127.0.0.1:${server.port}/admin
再在啟動類上加上 @EnableAdminServer 註解,齊活,完事。
通過如上介面,我們可以清楚的看到應用的名稱和 IP 埠資訊,應用名稱是預設的,也可以在配置檔案中通過 spring.application.name 來自定義名稱。
我們還可以點選服務資訊進去檢視詳情,左邊是對應的功能選單,右邊是資料展示的頁面。詳情中有健康資訊、執行緒資訊、JVM 記憶體等資訊,都通過圖形的方式展示,一目瞭然。
通過左側功能選單可以看到還有 日誌、JVM、快取 等管理功能,大家可以點點看看。
2.4、Spring Boot Starter 開箱即用
關於 Spring Boot Starter 相信大家一定不陌生,很多第三方工具的引入都是涉及到 Starter 包,Starter 包可以說是 Spring Boot 中的核心功能,Starter 的出現,簡化了 Spring 很多工作。不懂就問環節:什麼是 Starter?
大家可以看一下我轉載的這篇文章:
https://www.cnblogs.com/niceyoo/p/14022406.html
總之,Starter 包簡化框架整合難度,將 Bean 的自動裝配邏輯封裝在 Starter 包內部,同時也簡化了 Maven Jar 包的依賴,對框架的整合只需要加入一個 Starter 包的配置,降低了煩瑣配置的出錯機率。
如下是 Spring Boot 提供的開箱即用 Starter 包:
starter | desc |
---|---|
spring-boot-starter-web | 用於快速構建Web,包含 RESTful 風格框架、SpringMVC和預設的嵌入式容器Tomcat |
spring-boot-starter-test | 用於測試 |
spring-boot-starter-data-jpa | 帶有Hibermate的Spring Data JPA,用於運算元據庫。 |
spring-boot-starter-jdbc | 傳統的JDBC支援 |
spring-boot-starter-thymeleaf | 支援Thymeleaf模板 |
spring-boot-starter-mail | 支援Java Mail、Spring Email 傳送郵件 |
spring-boot-starter-integration | Spring框架建立的一個API,面向企業應用整合(EAI) |
spring-boot-starter-mobile | SpringMVC的擴充套件,用來簡化手機上的Web應用程式開發 |
spring-boot-starter-data-redis | 快速整合並操作 Redis:通過Spring Data Redis、Redis Client使用Redis |
spring-boot-starter-validation | Bean Validation是一個資料驗證的規範,Hibernate Validator是一個資料驗證框架 |
spring-boot-starter-websocket | 相對於非持久的協議HTTP,Websocket 是一個持久化的協議 |
spring-boot-starter-web-services | SOAP Web Services |
spring-boot-starter-hateoas | 為服務新增HATEOAS功能 |
spring-boot-starter-security | 用Spring Security進行身份驗證和授權 |
spring-boot-starter-data-rest | 用Spring Data REST公佈簡單的REST服務 |
3、Spring Boot 專案的建立方式
建立 Spring Boot 有兩種方式:
- 手動建立一個 Maven 專案,然後新增 Spring Boot 需要的依賴;
- 通過 Spring Initializr 腳手架建立;
工具環境 IDEA,下文是基於 Spring Initializr 腳手架建立。
通過 IDEA 建立 Spring Boot 專案實在是太簡單了:
關於下一步之後,無非就是選擇建立專案的方式(Maven/Gradle),Spring Boot 版本(2.2.2.RELEASE),引入的依賴(Web/SQL/NoSQL/Security等),在這就不贅述了。
4、Spring Boot 編譯打包
Spring Boot 專案打包無非就 「打 jar 包、打 war 包」 這兩種情況,這兩者的應用場景不同,各有優缺點。
jar 包部署優點:
- 無須搭建本地web容器,預設內建 Tomcat 容器,可以直接以 java -jar 形式執行;
- 因為自帶web容器,可以避免由於web容器的差異造成不同環境結果不一致問題。
- 藉助容器化,可以進行大規模的部署。
jar 包部署缺點:
- jar 應用體積過大「可以參考瘦身指南」
- 資料來源無法通過介面進行管理。
- 修改web容器相關配置比較困難,需要藉助程式碼實現。
war 包部署優點:
- 可以藉助web容器管理介面對應用進行管理。
- web容器配置較為靈活,配置和程式分離。「jar包方式其實也可以」
- 應用體積較小,甚至可以藉助web容器的包管理功能進一步減小應用大小。
war 包部署缺點:
- 本地需要搭建web容器
- 除錯較為困難,需要藉助web容器。
- 部署較為困難
至於最終選擇哪個,本文不追究,畢竟都是要根據實際專案來說的。
以打 jar 包為例:
首先需要在 pom.xml 檔案中配置 spring-boot-maven-plugin 打包外掛,也就是我們通常看到的:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
這樣就可以在 Tmerinal 控制檯通過 maven 命令進行打包了:mvn package
或者是可以直接在 IDEA 右側的 Maven 標籤:
有小夥伴可能好奇,難道不需要設定 <packaging>jar</packaging>
嗎?
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<packaging>jar</packaging>
...
</project>
其實預設的 就是 jar 方式。
5、Spring Boot 多環境配置
通常在實際開發中,往往涉及到好幾個環境,比如開發環境、本地環境、測試環境等等,通常不同環境下的配置資訊是不一樣的「比如埠號?資料庫連線資訊等」,因為我們每次只能使用一個配置環境,如果頻繁的修改配置以達到效果,自然是麻煩的不行,且很容易出錯。
而在 Spring Boot 中,我們可以通過 spring.profiles.active 來啟用對應的配置檔案。
比如建立如下三個檔案:
application.yml「主檔案」
- application-dev.yml:開發環境
- application-local.yml:本地環境
- application-test.yml:測試環境
我們想要使用 dev 開發環境,只需要在 application.yml 中使用 spring.profiles.active=dev 就可以完成指定:
spring:
profiles:
active: dev
6、替換內建的 Tomcat 容器
儘管Spring Boot內建了 Tomcat ,但是在高併發場景下的 Tomcat 相對來說比較弱。比如在相同的機器配置下,模擬相等的請求數,Undertow在效能和記憶體使用方面都是最優的。並且Undertow新版本預設使用持久連線,這將會進一步提高它的併發吞吐能力。所以,如果是高併發的業務系統,Undertow 是最佳選擇。
問題是怎麼替換?
因為 spring-boot-starter-web 中預設自帶的容器是 Tomcat,如果想要替換成 Undertow 也是非常簡單的。
首先需要排除 spring-boot-starter-web 包中的 Tomcat,然後單獨增加 undertow 容器的依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 排除Tomcat依賴 -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 新增 Undertow依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
7、配置檔案的讀取
以前讀取 spring 的配置檔案,都是通過工具類類讀取,其實無非就是讀取 xml 檔案,比如,通過 ClassPathXmlApplicationContext 載入到檔案,然後再通過 上下文拿到 對應的Bean,再取參等。
而在 Spring Boot 中讀取配置檔案有三種方式:
-
Environment
-
@Value
-
@ConfigurationProperties
我們一一看看這三種方式:
application.yml 中的模擬資料:
niceyoo:
name: 張三
age: 24
7.1、Environment
Environment 用於管理當前的應用環境資訊,定義了獲取 Profile 的方法,同時繼承了 PropertyResolver,PropertyResolver 提供了屬性訪問的相關方法。
也就是我們可以使用 Environment 的 getProperty() 方法讀取指定配置 Key 的內容。
程式碼中體現:
@Controller
@RequestMapping(value = "/test")
public class TestController {
@Autowired
private Environment environment;
@PostMapping("/test")
@ResponseBody
public void test(@RequestBody User user) {
String name = environment.getProperty("niceyoo.name");
Integer age = Integer.valueOf(environment.getProperty("niceyoo.age"));
System.out.println(name+" " +age);
}
}
7.2、@Value
@Value 方式就簡單多了,直接在接收的屬性上加上該註解即可:
@Controller
@RequestMapping(value = "/test")
public class TestController {
@Value("niceyoo.name")
private String name;
@Value("niceyoo.age")
private Integer age;
@PostMapping("/test")
@ResponseBody
public void test(@RequestBody User user) {
System.out.println(name+" " +age);
}
}
7.3、@ConfigurationProperties
使用該註解可以直接注入到實體類中,方便值的同一管理。
比如我們建立一個 Model 實體類,定義 name、age 屬性,然後實現 get\set 方法,再在實體類上加上 @Configuration 和 @ConfigurationProperties(prefix=“niceyoo”) 註解,並指定字首為 niceyoo。
@Configuration
@ConfigurationProperties(prefix="niceyoo")
public class Model {
private String name;
private Integer age;
省略get、set方法
}
這樣就可以將 niceyoo 下面的值直接對應到實體上了,其中加入 @Configuration 註解,我們在使用時可以直接通過 @Autowired 進行注入。
取值程式碼:
@Controller
@RequestMapping(value = "/test")
public class TestController {
@Autowired
private Model model;
@PostMapping("/test")
@ResponseBody
public void test(@RequestBody User user) {
System.out.println(model.getName()+" " +model.getAge());
}
}
總結
以前 Spring 主打輕量級應用框架,但隨著外界不斷地進行擴充,像是 Shiro、Security、MQ、Redis、ElasticSearch 等等等等, 總之就是 Spring 幾乎可以做任何事了,但是相應的問題也來了。
Spring 每整合一個第三方軟體,就需要手動增加一些配置,隨著新軟體的加入,以至於需要引入越來越多的配置,且這些配置各玩各的,難以理解,難以管理,我記得我實習時的那家公司就是這樣,各種五花八門的配置檔案,以至於經常配置出錯,然後解決配置相關的問題就需要好久。
工欲善其行,必先利其器。
Spring Boot 的出現可以說真正的顛覆了以前傳統的 Web 專案,開發人員再也不用配置繁瑣的 xml 檔案了,簡直是解放了雙手,整個專案的配置檔案也變得簡潔了。
正所謂簡潔並不意味著簡單,Spring Boot 只是將眾多複雜的功能進行了封裝,讓我們在使用的時候足夠的簡單。
關於 Spring Boot 的知識點可以說太多太多了,文中只是把自己能想到的幾點描述了出來。
歡迎大家在留言區互動,
部落格園持續更新,歡迎關注。希望這篇文章對大家有所幫助。
相關文章
- Spring Boot下Profile的四種切換方式思路總結Spring Boot
- MySQL 5.7傳統複製到GTID線上切換(一主一從)MySql
- Spring Boot + Vue 前後端分離,兩種檔案上傳方式總結Spring BootVue後端
- 從NodeJS切換到Ruby on Rails - nikodunkNodeJSAI
- 如何從 Docker Desktop 切換到 ColimaDocker
- Spring Boot從入門到實戰:整合Web專案常用功能Spring BootWeb
- Spring Boot 配置檔案總結Spring Boot
- Spring Boot + JPA學習總結Spring Boot
- spring-boot-route(十)多資料來源切換Springboot
- Spring Boot入門(四):開發Web Api介面常用註解總結Spring BootWebAPI
- 使用Spring Boot開發Web專案Spring BootWeb
- Spring boot常用命令總結Spring Boot
- Spring Boot+Mybatis專案總結Spring BootMyBatis
- Spring Boot 的 Web 開發Spring BootWeb
- 從log4j切換到logback後專案無法啟動
- App切換到後臺後如何保持持續定位?APP
- 實戰:如何優雅的從 Skywalking 切換到 OpenTelemetry
- 為什麼Discord從Go切換到Rust?GoRust
- ABP VNext從單體切換到微服務微服務
- 我將從VS Code切換到VS Codium
- 使用Spring Boot RESTful Web流式資料 | TechshardSpring BootRESTWeb
- JavaWeb基礎知識總結:如何系統學習spring boot?JavaWebSpring Boot
- Spring Boot中使用JavaMailSender傳送郵件Spring BootJavaAI
- 文字框填內容寫達到指定長度自動切換
- 為什麼我們從Yarn切換到pnpmYarnNPM
- Spring Cloud使用總結SpringCloud
- 傳統方法總結
- 【流式傳輸】使用Spring Boot實現ChatGpt流式傳輸Spring BootChatGPT
- 使用 Spring Boot 和 @WebMvcTest 測試 MVC Web ControllerSpring BootWebMVCController
- Spring Boot中自定義註解+AOP實現主備庫切換Spring Boot
- Spring Boot之Validation自定義實現總結Spring Boot
- Spring Boot 整合 Shiro ,兩種方式全總結!Spring Boot
- Spring boot/Spring 統一錯誤處理方案的使用Spring Boot
- Spring Boot的檔案上傳Spring Boot
- Spring Boot2從入門到實戰:統一異常處理Spring Boot
- Spring MVC 到 Spring Boot 的簡化之路MVCSpring Boot
- Vue + Spring Boot——axios使用GET以引數的方式傳遞物件到SpringMVC解決方案VueSpring BootiOS物件SpringMVC
- 從VPS切換到雲伺服器的幾大理由伺服器