開發(fā) RESTful 應(yīng)用程序,已經(jīng)從困難到容易
本文轉(zhuǎn)載自微信公眾號「七哥聊編程」,作者七哥 。轉(zhuǎn)載本文請聯(lián)系七哥聊編程公眾號。
大家好呀,我是七哥。
大家都知道,我們要用 Spring 創(chuàng)建一個RESTful 應(yīng)用程序,是需要為框架做很多事情的,比如往項目的構(gòu)建文件中添加各種庫依賴,還要自己寫各種配置文件,因為 Spring 雖然牛逼的提供了很多優(yōu)秀的特性,但是我們還是需要配置告訴 Spring 該怎么做。
但是,現(xiàn)在都啥時代了,全人類都講究自動化、智能化了,我們程序員開發(fā)任務(wù)為啥就不能來點自動化、智能化呢?這不, Spring Boot 橫空出世了。它擁有的 起步依賴、自動配置 特性,讓我們拒絕那些繁瑣且千篇一律的配置,解放程序員的雙手,集中精力開發(fā)應(yīng)用程序即可。
今天這篇文章的目標,就是帶大家用 Spring Boot 開發(fā)一個視頻觀看列表的應(yīng)用程序,一起感受 Spring Boot 的強大。
1. 前言
我們今天要開發(fā)的這個視頻觀看列表應(yīng)用程序,可以支持用戶查找觀看列表,輸入自己想看的視頻信息,刪除已經(jīng)看過的視頻。
技術(shù)棧采用 Spring Boot 快速開發(fā)、Spring MVC 處理 Web 請求, Thymeleaf 來定義 Web 視圖,Spring Data JPA 來持久化觀看列表到數(shù)據(jù)化,數(shù)據(jù)庫使用 H2。
2. 初始化項目
創(chuàng)建 Spring Boot 項目的幾種方式我們之前已經(jīng)講過了,本質(zhì)都是使用的 Spring Initializr,選擇最合適的就好,但是要確保勾選了我們需要的依賴。
使用 IDEA 選擇 Initializr 創(chuàng)建項目:
創(chuàng)建完成后,就得到了如下圖的項目結(jié)構(gòu):
基本上 Spring Boot 項目初始化完成后的結(jié)構(gòu)都是一樣的,你可能已經(jīng)著急想開發(fā)應(yīng)用程序了,但是在進入下一步寫代碼之前,我們還是停下腳步來具體看下項目里的這些東西都是干啥的,我覺得這是很重要的!搞清楚了大概的原理在開發(fā)的時候就不容易踩坑了,免得你在實際工作中不明所以,改這些文件也是糊里糊涂的,這樣容易出 bug 的~~
3. Spring Boot項目的結(jié)構(gòu)
總體來說 Spring Boot 項目是遵循 Maven 或者 Gradle 的項目布局的,主要代碼放在 src/main/java 路徑下,配置文件放到 src/main/resources 路徑下,測試代碼放到 src/test/java 路徑下,如果有測試配置就放在 src/test/resources 路徑。
我們再來看下我們項目根目錄中還有不少文件:
- build.gradle: Gradle 構(gòu)建說明文件;
- SpringRoad02Application.java:應(yīng)用程序的啟動引導(dǎo)類,同時也是主要的 Spring 配置類;
- application.properties:用于配置應(yīng)用程序和 Spring Boot 的屬性;
- SpringRoad02ApplicationTests.java:基本的集成測試類;
啟動引導(dǎo)Spring
SpringRoad02Application 它的作用是啟動引導(dǎo)和配置。
- //開啟組件掃描和自動配置
- @SpringBootApplication
- public class SpringRoad02Application {
- public static void main(String[] args) {
- // 啟動引導(dǎo)應(yīng)用程序
- SpringApplication.run(SpringRoad02Application.class, args);
- }
- }
@SpringBootApplication 這個注解是如何開啟組件掃描和自動配置功能的呢?
這是因為 @SpringBootApplication 注解將三個有用的注解組合在了一起:
- @SpringBootConfiguration:標明該類是一個基于 Java 代碼的 Spring 配置類;
- @ComponentScan:啟用組件掃描,這樣項目中我們寫的控制器和其它服務(wù)類才會被 Spring 掃描注冊為應(yīng)用程序上下文中的 Bean;
- @EnableAutoConfiguration:這個注解最牛逼,就是它開啟了 Spring Boot 的黑魔法,讓我們不用寫 Spring 那些繁瑣的 xml 配置;
那要運行 Spring Boot 應(yīng)用,除了傳統(tǒng)的 WAR 包部署,啟動類的 main 方法支持我們在命令行里把應(yīng)用程序當成可執(zhí)行的 JAR 包來運行。實際上就算我們一行代碼都不寫,這個項目已經(jīng)是可運行的了,最簡單的構(gòu)建運行方法就是 Gradle 的 bootRun 任務(wù),你可以執(zhí)行下面的命令感受一下:
- gradle bootRun
啟動成功截圖:
測試Spring Boot應(yīng)用程序
Initializr 還提供了一個測試類:SpringRoad02ApplicationTests ,可以基于它我們來為應(yīng)用程序編寫測試。
- @SpringBootTest // 通過 Spring Boot 加載上下文
- class SpringRoad02ApplicationTests {
- @Test
- void contextLoads() {
- }
- }
可以直接運行這個 contextLoads() 方法,不報錯就說明加載應(yīng)用程序上下文是沒有問題的:
配置應(yīng)用程序?qū)傩?/strong>
Initializr 生成的項目中有一個空的 application.properties 文件,這個文件其實是可選的,你可以刪掉,但是一般會保留,因為實際項目肯定都有很多的屬性配置,比如數(shù)據(jù)庫、mq、redis 等等。
我們可以給這個文件配置一個屬性 sever.port=8000 試試看:
加上后,我們在啟動程序,它內(nèi)嵌的 Tomcat 監(jiān)聽端口就從默認的 8080 改為了 8000。
神奇的地方在于,我們完全不用告訴 Spring Boot 去加載 application.properties 文件,只要它在根目錄存在就會自動被加載,Spring 和 應(yīng)用程序就可以獲取其中配置的屬性。
4. 構(gòu)建說明文件,起步依賴介紹
項目基本結(jié)構(gòu)我們上面都已經(jīng)介紹的差不多了,那接下里我們就來看看 Spring Boot 應(yīng)用程序到底是如何構(gòu)建的。比如 Spring Boot 為什么可以支持打 JAR 包部署?如果沒有 Spring Boot 我們要使用 Spring MVC 需要添加哪個依賴,應(yīng)該用哪個版本的 Spring Data JPA?這些包放在一起的兼容性如何?
上面這些問題,都是我們在開發(fā) Spring 應(yīng)用程序會遇到的問題,一般情況下項目需要依賴包我們都是直接從其它項目拷貝過來,因為現(xiàn)有項目經(jīng)過了測試,依賴jar包之前的兼容性經(jīng)過了測試,出現(xiàn)問題的情況就比較小。不過就算這樣,這也是一項復(fù)雜繁瑣的工作。
好在 Spring Boot 的出現(xiàn)解放了我們奮戰(zhàn)在一線的程序員,因為它引入了起步依賴這個特性,簡而言之就是可以根據(jù)功能來引入對應(yīng)的依賴包。
比如我們要開發(fā)一個 Web 應(yīng)用,它使用 Thymeleaf,通過 Spring Data JPA 來持久化數(shù)據(jù),那我們的構(gòu)建文件就只需要寫上這些功能就可以了。
比如下面這樣,就是我通過 Spring Initializr 創(chuàng)建 Spring Boot 應(yīng)用程序后 Gradle 構(gòu)建文件里的包依賴說明:
- dependencies {
- implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
- implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
- implementation 'org.springframework.boot:spring-boot-starter-web'
- compileOnly 'org.projectlombok:lombok'
- runtimeOnly 'com.h2database:h2'
- annotationProcessor 'org.projectlombok:lombok'
- testImplementation 'org.springframework.boot:spring-boot-starter-test'
- }
可以看到,構(gòu)建文件中沒有單獨的庫依賴,只需要添加對應(yīng)的 起步依賴 就好了。這幾個起步依賴等價于加了一大把獨立的包,每個包的版本會根據(jù) Spring Boot 的版本自動幫我們決定,都是經(jīng)過大量的官方測試的,所以兼容性也可以放心。
如果你是一個小心謹慎的人,實在想看看自己用的相關(guān)包的版本,那么可以使用構(gòu)建工具的命令查到你想看的:
- // gradle里使用的命令
- gradle dependencies
- // maven里使用的命令
- mvn dependency:tree
然后你就會得到一顆樹狀列表,查看每個起步依賴引入的那一大堆相關(guān)的包。
上面的這幾個起步依賴,包括 Web、Thymeleaf、JPA等,只不過是 Spring Boot 眾多起步依賴中的滄海一粟,它還提供了很多起步依賴,大家可以按需添加即可。
覆蓋起步依賴引入的傳遞依賴
假如我們現(xiàn)在要替換起步依賴引入的其中一個包的版本,要怎么做呢?當然一般我們不需要手動去替換,但是方法我們還是需要掌握的,可以用在某個包出現(xiàn)了嚴重的bug,或者刪除不需要使用的包給項目瘦身。
排除傳遞的依賴
gradle里面可以如下操作:
- // 排除掉 Jackson
- implementation ('org.springframework.boot:spring-boot-starter-data-jpa') {
- exclude group: 'com.fasterxml.jackson.core'
- }
maven里可以使用元素來排除傳遞依賴。
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- <exclusions>
- <exclusion>
- <groupId>com.fasterxml.jsackson.core</groupId>
- </exclusion>
- </exclusions>
- </dependency>
替換傳遞的依賴
假如我們是需要將傳遞的 Jackson 包進行升級呢?這種情況一般多用在新版本的包升級了某個bug。
Maven 總是會用最近的依賴,也就是說重新添加就會覆蓋:
- <dependency>
- <groupId>com.fasterxml.jsackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- <version>2.12.3</version>
- </dependency>
Gradle 也是一樣的道理,不過如果你要替換的包版本比傳遞的包版本低 ,那么這塊有點坑需要注意下:Gradle 默認使用最新版本的包,所以就需要先排除起步依賴引入的包,在加入對應(yīng)版本的包。
- implementation ('org.springframework.boot:spring-boot-starter-data-jpa') {
- exclude group: 'com.fasterxml.jackson.core'
- }
- implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.1'
當然替換包的版本時候一定要謹慎,畢竟起步依賴的傳遞都是經(jīng)過大量的測試的。
5. 使用自動配置,代碼開發(fā)
Spring Boot 采用的自動配置,可以節(jié)省我們手動寫諸如 web.xml 等配置文件,下面直接給大家展示下我們開發(fā)一個視頻觀看列表程序所需要的代碼。
數(shù)據(jù)庫實體對象;
- @Entity
- @Data
- public class Video {
- // 主鍵
- @Id
- // 值是自動生成的
- @GeneratedValue(strategy = GenerationType.AUTO)
- private Long id;
- private String reader;
- private String isbn;
- private String title;
- private String author;
- private String description;
- }
@Entity 注解表示這是一個JPA實體對象,@GeneratedValue 表示 id 字段的值是自動生成的。
數(shù)據(jù)訪問接口
- public interface ReadingListRepository extends JpaRepository<Video,Long> {
- List<Video> findByReader(String reader);
- }
你沒看錯,只需要繼承 JpaRepository 接口即可,這個接口內(nèi)置了18個方法,我們也無需寫實現(xiàn)類, Spring Date 提供了非常智能的實現(xiàn),應(yīng)用程序啟動后,就會幫我們自動生成實現(xiàn)。
Web 接口定義
有了數(shù)據(jù)庫實體對象和持久化數(shù)據(jù)的接口,接下來我們就需要一個前端控制器,這塊使用的當然就是 SpringMVC 了。
- @Controller
- public class ReadingListController {
- @Autowired
- private ReadingListRepository readingListRepository;
- @GetMapping(value = "/{viewer}")
- public String readersVideos(@PathVariable("viewer") String viewer, Model model) {
- List<Video> videoList = readingListRepository.findByReader(viewer);
- if (videoList != null) {
- model.addAttribute("videos", videoList);
- }
- return "readingList";
- }
- @PostMapping(value = "/{viewer}")
- public String addToReadingList(@PathVariable("viewer") String viewer, Video video) {
- video.setReader(viewer);
- readingListRepository.save(video);
- return "redirect:/{viewer}";
- }
- }
代碼很簡單,就是定義了兩個接口,一個用于處理用戶的 GET 請求,獲取觀看視頻的列表,一個用于將用戶提交的視頻信息綁定到 Video 對象,然后通過數(shù)據(jù)訪問接口落庫。
由于 Spring Boot 提供的自動裝配特性,因此我們無需配置視圖解析器和模板引擎,就可以支持解析邏輯視圖名然后重定向到頁面了。
Thymeleaf模板
Spring Boot 項目我們的視圖模板文件需要放到 src/main/resources/templates 目錄下,文件名為:readingList.html 。
- <html>
- <head>
- <title>Reading List</title>
- <link rel="stylesheet" th:href="@{/style.css}"></link>
- </head>
- <body>
- <h2>Your Reading List</h2>
- <div th:unless="${#lists.isEmpty(videos)}">
- <dl th:each="video : ${videos}">
- <dt class="bookHeadline">
- <span th:text="${video.title}">Title</span> by
- <span th:text="${video.author}">Author</span>
- (ISBN: <span th:text="${video.isbn}">ISBN</span>)
- </dt>
- <dd class="bookDescription">
- <span th:if="${video.description}" th:text="${video.description}">Description</span>
- <span th:if="${video.description eq null}">No description available</span>
- </dd>
- </dl>
- </div>
- <div th:if="${#lists.isEmpty(videos)}">
- <p>You have no books in your book list</p>
- </div>
- <hr/>
- <h3>Add a video</h3>
- <form method="POST">
- <label for="title">Title:</label>
- <input type="text" name="title" size="50"></input><br/>
- <label for="author">Author:</label>
- <input type="text" name="author" size="50"></input><br/>
- <label for="isbn">ISBN:</label>
- <input type="text" name="isbn" size="15"></input><br/>
- <label for="description">Description:</label><br/>
- <textarea name="description" cols="80" rows="5"></textarea><br/>
- <input type="submit"></input>
- </form>
- </body>
- </html>
然后我們還提供了一個簡單的樣式文件,這個名為 style.css 的樣式文件,我們需要放到 src/main/resources/static 靜態(tài)資源目錄下。
- body {
- background-color: #cccccc;
- font-family: arial,helvetica,sans-serif;
- }
- .bookHeadline {
- font-size: 12pt;
- font-weight: bold;
- }
- .bookDescription {
- font-size: 10pt;
- }
- label {
- font-weight: bold;
至此代碼就寫完了,雖然它什么配置都沒有寫,不過這確實已經(jīng)是一個完整的 Spring 應(yīng)用程序了。你是不是已經(jīng)迫不及待的想運行了?
6. 運行測試
Spring Boot 應(yīng)用程序,可以選擇通過 Maven 或者 Gradle 來運行應(yīng)用程序,比如文章前面我們使用的: gradle bootRun。
當然我們一般使用 IDEA 來開發(fā),可以執(zhí)行運行啟動類的 main 方法來運行。
然后我們通過瀏覽器來訪問:http://localhost:8000/sevenluo
可以嘗試添加一些視頻,然后就可以得到視頻列表了。
結(jié)尾
那今天我們通過一個簡單的視頻觀看列表應(yīng)用程序,帶大家手把手開發(fā)了一個完整的 Spring Boot 應(yīng)用程序。
開發(fā)全程我們是沒有任何配置的,之所以這么便利主要是用到了 Spring 4.0 引入的條件化配置新特性。
我們也可以在 Spring 里很方便的編寫自己的條件,只要實現(xiàn) Condition 接口即可,今天就不多逼逼了,如果你對于 Spring Boot 如何通過條件化配置實現(xiàn)自動裝配感興趣,可以留言告訴我,我們安排一章來詳細介紹。