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

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);

}

}

參考資料

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 PackJava 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>