構建一個Spring Boot
應用
先決條件
-
Java
開發神器IDEA
-
JDK 1.8
或更高版本 -
Maven 3.2+
或者Gradle 4+
通過Maven
來構建
如果您不還熟悉maven
,請參閱 使用Maven構建Java項目。
接下來,我們通過IDEA
來快速創建maven
項目。
- 點擊
New->Project...
- 選擇
Maven
,點擊Next
- 輸入
GroupId
、ArtifactId
、Version
,點擊Next
- 輸入
Project name
、Project location
,點擊Finish
- 項目創建成功后,你會得到如下界面
添加classpath
依賴項
pom.xml
描述了用于構建項目的策略,為了使得我們的項目支持spring-boot
,我們需要在pom.xml
添加如下依賴:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sunmi</groupId>
<artifactId>sping-boot-study</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<!--父節點啟動器-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
<dependencies>
<!--spring-boot依賴項-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
Spring Boot
提供了許多“Starters”
,可以將jar
添加到classpath
中。 我們的示例應用程序已經在pom.xml
文件中指定parent
為spring-boot-starter-parent
。 spring-boot-starter-parent
是一個特殊的啟動器,提供有用的maven
默認值。 它還提供了一個依賴項的version
,以便您可以省略依賴項的版本標記。
創建一個Application
類
在這里,您將使用組件創建一個應用程序類:
src/main/java/hello/Application.java
package hello;
import java.util.Arrays;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
};
}
}
@SpringBootApplication
是一個便利注釋,添加了以下所有內容:
-
@Configuration
標記該類作為應用程序上下文的bean
定義的來源。 -
@EnableAutoConfiguration
告訴Spring Boot
開始根據classpath
設置,其他bean
和各種屬性設置添加bean
。 - 通常你會為
Spring MVC
應用添加@EnableWebMvc
注解,但Spring Boot
會在classpath
上看到webmvc
時自動添加它。 這會將應用程序標記為Web
應用程序,并激活關鍵行為以及設置調度程序servlet
。
這會將應用程序標記為Web
應用程序,并在安裝DispatcherServlet
時激活關鍵行為。 -
@ComponentScan
告訴Spring
在hello
包中尋找其他components
,configurations
和services
。
main()
方法使用Spring Boot
的SpringApplication.run()
方法來啟動應用程序。 您是否注意到沒有一行XML
? 也沒有web.xml
文件。 此Web
應用程序是100%
純Java
,您無需處理配置任何管道或基礎結構。
還有一個標記為@Bean
的CommandLineRunner
方法,它在spring boot
啟動時運行。 示例中它將檢索由您的應用程序創建或Spring Boot
自動添加的所有bean
。 它對它們進行分類并打印出來。
運行該應用程序
mvn spring-boot:run
創建一個簡單的Web
應用程序
現在,我們為簡單的Web
應用程序創建Web controller
:
src/main/java/hello/HelloController.java
package hello;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
public class HelloController {
@RequestMapping("/")
public String index() {
return "Greetings from Spring Boot!";
}
}
該類被標記為@RestController
,這意味著Spring MVC
可以使用它來處理Web
請求。 @RequestMapping
將/
映射到index()
方法。 從瀏覽器訪問或在命令行上使用curl
時,該方法返回純文本。 這是因為@RestController
結合了@Controller
和@ResponseBody
,兩個注釋導致Web
請求返回數據而不是視圖。
檢查服務
- 第一個終端執行
mvn spring-boot:run
- 第二個終端執行
$ curl localhost:8080
Greetings from Spring Boot!
添加單元測試
您將需要為endpoint
添加測試,Spring Test
已經為此提供了一些機制,并且很容易包含在您的項目中。
在maven
中加入如下依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
現在編寫一個簡單的單元測試,通過endpoint
模擬servlet
請求和響應:
package hello;
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void getHello() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo("Greetings from Spring Boot!")));
}
}
MockMvc
對象來自Spring Test
,它允許您通過一組便利構造器類將HTTP
請求發送到DispatcherServlet
并對結果進行斷言。 請注意@AutoConfigureMockMvc
與@SpringBootTest
一起使用以注入MockMvc
實例。 使用@SpringBootTest
后,web
應用程序會被自動創建。
除了模擬HTTP
請求周期之外,我們還可以使用Spring Boot
編寫一個非常簡單的全棧集成測試。 例如,我們可以這樣做,而不是上面的模擬測試:
package hello;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.net.URL;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HelloControllerIT {
@LocalServerPort
private int port;
private URL base;
@Autowired
private TestRestTemplate template;
@Before
public void setUp() throws Exception {
this.base = new URL("http://localhost:" + port + "/");
}
@Test
public void getHello() throws Exception {
ResponseEntity<String> response = template.getForEntity(base.toString(),
String.class);
assertThat(response.getBody(), equalTo("Greetings from Spring Boot!"));
}
}
嵌入式服務器通過webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
在隨機端口上啟動,并在運行時使用@LocalServerPort
發現實際端口。
創建一個可執行的Jar
我們通過創建一個包含全部依賴的可執行jar
文件來完成我們的示例,我們可以在生產中運行它。 可執行jar
(有時稱為“fat jar”
)是包含已被編譯的代碼以及需要的全部依賴jar的歸檔。
要創建可執行jar
,我們需要將spring-boot-maven-plugin
添加到我們的pom.xml
中。 為此,請在依賴項部分下方插入以下行:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
接著在終端執行如下命令
mvn package
查看target
目錄,則應該看到sping-boot-study-1.0-SNAPSHOT.jar
。 該文件大小應為10 MB
左右。 如果你想查看jar
的內部,你可以使用jar tvf
,如下所示:
jar tvf target/sping-boot-study-1.0-SNAPSHOT.jar
要運行該應用程序,請使用java -jar
命令,如下所示:
java -jar target/sping-boot-study-1.0-SNAPSHOT.jar
和以前一樣,要退出應用程序,請按ctrl-c
。
Spring-boot
使用MySQL
訪問數據
創建數據庫
打開終端,例如在Linux/Mac
上,我們使用命令
$ sudo mysql --password
這以管理員身份連接到MySQL
的bash
。
- 創建一個新的數據庫
mysql> create database db_example; -- 創建新的數據庫
mysql> create user 'springuser'@'localhost' identified by 'ThePassword'; -- 創建一個用戶
mysql> grant all on db_example.* to 'springuser'@'localhost'; -- 對新創建的數據庫的所有權限給予新創建的用戶
- 查看創建的數據庫
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| db_example |
| mydatabase |
| mysql |
| performance_schema |
| sys |
+--------------------+
6 rows in set (0.00 sec)
- 查看新創建的
user
mysql> SELECT User FROM mysql.user;
+---------------+
| User |
+---------------+
| springuser |
| mysql.session |
| mysql.sys |
| root |
+---------------+
4 rows in set (0.00 sec)
配置spring-boot
- 修改
src/main/resources/application.yml
文件
spring:
jpa:
hibernate:
ddl-auto: update
datasource:
url: jdbc:mysql://localhost:3306/db_example
username: springuser
password: ThePassword
spring.jpa.hibernate.ddl-auto
可以是none
,update
,create
,create-drop
,有關詳細信息,請參閱Hibernate文檔。
-
none
: 這是MySQL
的默認值,不會更改數據表結構。 -
update
:Hibernate
根據給定的實體結構更改數據表結構。 -
create
每次創建數據表,但在關閉時不會刪除數據表。 -
create-drop
創建數據表,然后在SessionFactory
關閉時刪除它。
我們這里以create
開頭,因為我們還沒有數據表結構。 第一次運行后,我們可以根據程序要求將其切換為update
或none
。 如果要對數據表結構進行一些更改,請使用update
。
在數據庫處于生產狀態后,您可以使用none并從連接到Spring應用程序的
MySQL
用戶撤消所有權限,然后只給他SELECT
,UPDATE
,INSERT
,DELETE
,這是一種很好的安全做法。
本指南最后詳細介紹了這一點。
- 在
pom.xml
添加如下依賴
<!--提供了spring boot java持久化API-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--提供了操作mysql的依賴-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
創建 @Entity
模型
src/main/java/hello/User.java
package com.sunmi.bean;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity // 這告訴Hibernate從這個類中創建一個表
@Data //省去get、set 等方法的編寫
public class User {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String email;
}
Hibernate
將把帶有@Entity
注解的實體類自動轉換為數據庫表的結構。
創建操作數據表的接口
package com.sunmi.repository;
import com.sunmi.bean.User;
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<User, Integer> {
}
spring
將會自動創建一個實現了 UserRepository
接口的 bean
,包括CRUD
等接口。
創建控制器
package com.sunmi.controller;
import com.sunmi.bean.User;
import com.sunmi.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(path = "/demo") // 這意味著訪問url以/demo開始
public class MainController {
//這意味著spring會自動生成userRepository bean對象并注入,我們用這個對象來和mysql交互
@Autowired
private UserRepository userRepository;
@PostMapping(path = "/add")
public String addNewUser(@RequestBody User user) {
userRepository.save(user);
return "Saved";
}
@GetMapping(path = "/add2")
public String addNewUser(String name
, @RequestParam String email) {
User user = new User();
user.setName(name);
user.setEmail(email);
userRepository.save(user);
return "Saved2";
}
@GetMapping(path = "/all")
public Iterable<User> getAllUsers() {
// 返回所有的用戶
return userRepository.findAll();
}
@GetMapping(path = "/delete")
public String delete(@RequestParam Integer id) {
userRepository.deleteById(id);
return "ok";
}
@GetMapping(path = "/find-by-id")
public Optional<User> find(@RequestParam Integer id) {
return userRepository.findById(id);
}
}
上面的示例沒有明確指定GET
與PUT
,POST
等,因為@GetMapping
是@RequestMapping(method = GET)
的快捷方式。 @RequestMapping
默認映射所有HTTP
操作。 使用@RequestMapping(method = GET)
或其他快捷方式注解來限制映射。
測試我們的服務
- 添加一個
user
對象
$ curl 'http://localhost:8080/demo/add2?name=First&email=someemail@someemailprovider.com'
響應應該如下
Saved
- 查詢所有的
user
$ curl 'http://localhost:8080/demo/all'
響應應該如下
[{"id":1,"name":"First","email":"someemail@someemailprovider.com"}]
- 更新用戶通過
id
$ curl --request POST --header "Content-Type: application/json" http://localhost:8080/demo/add --data '{"id":1,"name":"Test","email":"someemail@someemailprovider.com"}'
Saved
- 查詢用戶通過
id
$ curl 'http://localhost:8080/demo/find-by-id?id=1'
{"id":1,"name":"Test","email":"someemail@someemailprovider.com"}
- 刪除用戶通過
id
$ curl 'http://localhost:8080/demo/delete?id=1'
"ok"
$ curl 'http://localhost:8080/demo/find-by-id?id=1'
null
- 你可能注意到對于
add2
接口,其第二個參數帶有@RequestParam注解,這表明來自客戶端的請求必須包含該字段。
$ curl 'http://localhost:8080/demo/add2?name=First'
你將得到如下錯誤
{"timestamp":"2019-03-22T04:49:29.239+0000","status":400,"error":"Bad Request","message":"Required String parameter 'email' is not present","path":"/demo/add2"}>
如果只指定了email
字段
$ curl 'http://localhost:8080/demo/add2?email=someemail@someemailprovider.com'
Saved
$ curl 'http://localhost:8080/demo/all'
[{"id":1,"name":"First","email":"someemail@someemailprovider.com"},
{"id":2,"name":null,"email":"someemail@someemailprovider.com"}]
- 簡化服務端的編碼
當一個接口的參數過多時,我們可以通過@RequestBody
注解,將這些參數組合成一個bean對象,客戶端訪問只需要傳遞一個json
對象(其中的字段是可選的),服務端接收到請求后會自動將json
對象系列化為bean
。
訪問此接口,并傳遞json
對象
$ curl --request POST --header "Content-Type: application/json" http://localhost:8080/demo/add --data '{"name":"Second","email":"someemail@someemailprovider.com"}'
Saved
$ curl 'http://localhost:8080/demo/all'
[{"id":1,"name":"First","email":"someemail@someemailprovider.com"},
{"id":2,"name":null,"email":"someemail@someemailprovider.com"},
{"id":3,"name":Second,"email":"someemail@someemailprovider.com"}]
進行一些安全性更改
現在,當您處于生產環境中時,可能會遇到SQL
注入攻擊。 黑客可能會注入DROP TABLE
或任何其他破壞性SQL
命令。 因此,作為安全實踐,在將應用程序公開給用戶之前,請對數據庫進行更改。
mysql> revoke all on db_example.* from 'springuser'@'localhost';
這將撤消與Spring
應用程序關聯的用戶的所有權限。 現在Spring
應用程序無法在數據庫中執行任何操作。 我們不希望如此。
mysql> grant select, insert, delete, update on db_example.* to 'springuser'@'localhost';
這為Spring
應用程序提供了僅更改數據庫數據而不是數據表結構所需的權限。
并修改src/main/resources/application.yml
文件
spring.jpa.hibernate.ddl-auto=none
而不是第一次運行時用的create
。
如果要對數據庫進行更改,請重新授予權限,將spring.jpa.hibernate.ddl-auto
更改為update
,然后重新運行應用程序,然后再重新授予權限,并將spring.jpa.hibernate.ddl-auto
改為none
。 或者,更好的是,使用專用的遷移工具,如Flyway
或Liquibase
。
響應式的方式訪問 Redis
數據
待更新...
參考文檔
https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started-first-application.html
https://spring.io/guides/gs/spring-boot/#scratch
https://spring.io/guides/gs/accessing-data-mysql/
https://www.linuxprobe.com/mysql-show-all-users.html
https://blog.csdn.net/mccand1234/article/details/53456411
https://stackoverflow.com/questions/7172784/how-to-post-json-data-with-curl-from-terminal-commandline-to-test-spring-rest
https://github.com/spring-guides/gs-accessing-data-mysql/blob/master/complete/pom.xml