Spring
Spring
2021/06/26
2021/10/07 (更新內容)
開發環境與開發工具
Visual Studio Code (VS Code)
Visual Studio Code跟Visual Studio是不一樣的,VS Code就像Eclipse或Visual Studio一樣,是個開發環境,不一樣的是,VS Code很多功能都需要自己下載,Visual Studio很多功能都已經內建了。要能夠執行Spring Boot要先安裝Java Development Kit (JDK) (建議java 11),請詳參Visual Studio Code裡的說明。
可利用指令確認目前已安裝之java版本
java -version
資料庫的部份,建議使用MySQL (詳參: http://www.codedata.com.tw/database/mysql-tutorial-getting-started/)。
Spring Framework
Spring Framework是一個基於Java (J2EE)的MVC架構,也就是將一個web application分為模型層(Model)、展示層 (View)、控制層(Controller),通常Model中會有一般的類別或資料庫的存取邏輯,View則是與使用者的互動介面,Controller則是將Model與View串連起來的關鍵角色。過去是跟Struts、Hibernate並稱為SSH,然而,現在已經被Spring整合在一起,不再是三個不同的框架了。例如,Spring的Data JPA實作就是以Hibernate為基礎。不過,JPA用在相對複雜的資料表的存取還是多少有效率的問題,所以,越來越多人會盡量少用JPA。
Spring Boot
Spring Boot簡化了Spring Framework的設定,因為Spring Boot內建伺服器,所以,不需要下載及安裝伺服器。
可以利用SPRING INITIALIZR產生一個空的spring boot專案,Project請選用Maven,Language選用Java,並使用Boot 2.5.5 (2021/9),Group: com.example,Artifact:demo,Packaging:Jar,Java版本: 11,Dependencies: Web。範例裡的html是採用web,所以,如果沒有選web,還需要一些額外的設定。下載下來後,解壓縮在專案的檔案夾裡。
如果使用Visual Studio Code,也可以利用Spring Initializr extension 產生新的spring boot專案 (詳參: Getting Started with Java in VS Code & Spring Boot in Visual Studio Code)。
不管用哪個方法,都會產生一個空的專案,會有一個java檔: src/main/java/com/example/demo/DemoApplication.java,這是專案執行的起點。
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
一般而言,一個基本的java程式會有:
package com.example.demo;
public class DemoApplication {
public static void main(String[] args) {
}
}
在main裡面,利用SpringApplication.run來執行這個類別,並且將main收到的參數(args)傳遞給SpringApplication。基本上,執行這一行會啟動web server了。
SpringApplication.run(DemoApplication.class, args);
加了這一行,就必須加入對應的import。
package com.example.demo;
import org.springframework.boot.SpringApplication;
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
當我們加了@SpringBootApplication,Spring會自動scan這個這個package以下的目錄,所以,其他的檔案要放在com.example.demo下,或這在這個之下的目錄,如: com.example.demo.controller,(如: Structuring Your Code的建議)。
@在java中稱為Annotation,Annotation是java 5的新語法,雖然很多人將Annotation翻譯為註解,但是,Annotation跟Comment是完全不一樣的,Comment是純粹給人看的,而Annotation卻是給系統看的。當我們加了@SpringBootApplication,Spring boot會進行很多動作,例如:讀取一些設定檔,也會自動scan這個這個package以下的目錄,處理其他的Annotation (詳參: Spring Framework Annotations)。後面會看到更多的Annotation。在沒有Annotation之前,這些動作都必須靠很多的設定檔,現在這些動作都簡化了。
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
參考資料
Java annotation (wikipedia)
Spring Controller
基本上去執行DemoApplication是沒有太多的意義,因為,並沒有定義任何的網頁或任何的服務,Spring是個MVC的架構,也就是會把程式碼切割為Model、View以及Controller。
過去,使用java開發網頁也是跟php差不多,執行每個程式(稱為servlet)就會產生對應的網頁,這樣的作法,雖然簡單,但是,當系統越來越大的時候就變得很複雜。後來,就有servlet+JSP的概念出現了,將html(view)與java為主的servlet切割。後來,系統越來越大,就跟php一樣,開始有一些框架的出現,一開始,是三個框架,稱為SSH(Spring、Struts、Hibernate),後來慢慢地整合在一起,成為現在的Spring。當框架出現時,首先就是提供最佳實務並且簡化設定,這些都是長期的累積前人的智慧,一步一步的演進到今天的規模。(手寫Servlet 到 Spring MVC 的簡化之路)
現在,在Spring裡,是利用Controller來定義所有的網路服務,服務有兩個部分:URI (URL)以及對應的HTTP action (如: Get, Post, Put, Delete)。
要定義一個Controller,首先,在DemoApplication.java的同一個目錄下,新增一個controller的資料夾,在資料夾中新增一個MainController.java。
一個最基本的Spring Controller就是一個類別,首先,會定義package,現在開始請養成習慣把同樣類型的類別都放在同一個package,例如,將controller都放在controller的package裡,並將檔案放在對應的路徑 (src/main/java/com/example/demo/controller)裡。
package com.example.demo.controller;
然後,import等一下需要的兩個類別。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
接下來,會利用java的Annotation:「@」告訴Spring boot定義這個類別是個Controller,spring boot會藉著這個annotation產生應該有的設定。
@Controller
一般而言,在@Controller裡會利用Mapping來設定被呼叫的方法及對應的URI。常用的HTTP 方法有get、post、put、patch、delete。
我們先指定index這個方法的為一個HTTP Get服務 ,在Spring 4.3之後,可利用GetMapping來也就是指定這個Controller是HTTP GET以及相對的URI,(在Spring 4.3之前,是使用@RequestMapping,詳參: Spring @RequestMapping New Shortcut Annotations)。
@ResponseBody,就是將指定內容(HTML)回傳給瀏覽器。
@GetMapping(value = "/")
@ResponseBody
下面的範例,就是告訴web server,當瀏覽器要求"/"時,就顯示Hi。
src/main/java/com/example/demo/controller/MainController.java。
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MainController{
@GetMapping(value = "/")
@ResponseBody
public String index() {
return "<h1>Hi!</h1>";
}
}
在vs code下要執行這個專案,先選專案,再按左邊的Debug icon,在Debug menu下選:Debug (Launch)-DemoApplication
啟動後,在瀏覽器下打開:
http://localhost:8080
可以看到執行結果。
如果是希望去打開一個html檔案,就可以把@ResponseBody去掉,並把回傳內容改為檔案名稱。
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
//import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MainController{
@GetMapping(value = "/")
//@ResponseBody
public String index() {
return "index.html";
//return "<h1>Hi!</h1>";
}
}
index.html (src/main/resources/static/index.html)的內容,請注意,這個檔案不是放在controller的檔案夾裡。
<!DOCTYPE html>
<head lang="en">
<meta charset="UTF-8" />
<title>React with Spring REST</title>
</head>
<body>
<h1>HELLO</h1>
</body>
</html>
這邊就可以看到是把Controller跟View(網頁)分割了。只是,我們接下來不會使用網頁,而是利用react開發view。這樣的話,就不能只是使用單純的controller,而是進一步要使用REST Controller。
常見問題
無法執行spring專案
可以利用SPRING INITIALIZR產生一個空的spring boot專案,但無法找到對應的package。
DemoApplication.java:3: error: package org.springframework.boot does not exist
import org.springframework.boot.SpringApplication;
請安裝Spring Boot Extension Pack、Java Extension Pack。
找不到package
如果看到
The import org.springframework.web cannot be resolved
應該就是在產生專案時,忘了選Spring Web。在VSCode裡,可以利用spring Initializr提供的功能來編輯,在pom.xml裡按右鍵,選擇"Edit Starter",就可以挑所需要的外掛,"Spring Web"。這樣的好處是,不會放錯地方。
也可以打開pom.xml,找到這一段
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
手動加上所需要的package
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>