環境準備
- 系統:MacOS
- 開發:IntelliJ IDEA
- 語言:Java8
- 其它:Mysql、Redis
腳手架代碼
Spring提供了一個創建項目腳手架的官網,在這裏可以直接定製項目的框架代碼。例如:
注意:在Dependencies中,添加了Web依賴。
點擊【Generate Project】,即可下載項目框架代碼。
創建工程
將框架代碼包解壓後放到工作目錄。打開IDEA,點擊【File] -> 【Open】,打開對應目錄。
啓動工程
找到com.spring.demo.demo下的DemoApplication,右鍵點擊運行後,console中即可顯示Spring啓動的信息。
Controller
在傳統MVC架構中,Controller負責接收Http請求並返回相應的應答,這個應答可以是一個頁面,也可以是一個JSON對象。方便起見,本教程使用RestfulAPI爲例。
添加RestController
創建一個UserController,負責響應User相關的業務請求。對於純數據的API接口,使用@RestControll進行標註,這樣每一個API接口的返回值就會被轉化爲JSON結構的數據。
package com.spring.demo.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@RequestMapping("/user")
public String find() {
return "UserA";
}
}
重啓應用,在瀏覽器中請求http://localhost:8080/user,即可看到請求的返回內容:UserA。
路徑參數
對於帶路徑參數的請求,可以通過@PathVariable 標註來獲取參數值。如:
package com.spring.demo.demo.controller;
import org.springframework.web.bind.annotation.PathVariable;
...
@RestController
public class UserController {
@RequestMapping("/user/{id}")
public String find(@PathVariable int id) {
return "用戶ID[" + id + "]";
}
}
POST請求
對於POST請求,可以使用@PostMapping 來標註方法,接收的JSON數據需要使用@RequestBody來標註。
...
@PostMapping("/user")
public void create(@RequestBody UserCreateRequest user) {
System.out.println(user.getName());
}
...
這裏的UserCreateRequest,用來接收並轉換JSON數據。
package com.spring.demo.demo.dto;
public class UserCreateRequest {
private String name;
private boolean gender;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isGender() {
return gender;
}
public void setGender(boolean gender) {
this.gender = gender;
}
}
在IDEA的Terminal中,使用curl來進行測試。
curl -XPOST 'http://127.0.0.1:8080/user' -H 'Content-Type: application/json' -d'{"name":"用戶A","gender":0}'
參數校驗
有時我們需要對傳入參數進行非空或有效值的校驗,這個處理應該在正式進入controller前進行。
1. 添加@Validated標註
在SpringMVC中,對輸入參數進行校驗通常使用@Validated標註。
...
@PostMapping("/user")
public void create(@RequestBody @Validated UserCreateRequest user) {
System.out.println(user.getName());
}
...
2. 添加校驗規則標註@
在對應的字段上,加上對應的校驗規則標註。
package com.spring.demo.demo.dto;
import javax.validation.constraints.NotNull;
public class UserCreateRequest {
@NotNull
private String name;
private boolean gender;
// Getters & Setters
...
}
添加後重啓服務,發送不含name字段的POST請求,結果如下:
Service
MVC框架中,Controller負責接收請求和相應,Service則負責具體的業務處理,即Model層。
Service在定義是需要使用@Service標註,SpringBoot在啓動中將會註冊該Service並在Controller通過DI來實例化並使用該Service。
一般來說,我們會創建一個Service的接口類和一個對應的實現類。
IUserService
package com.spring.demo.demo.service;
public interface IUserService {
public String findUser(int id);
}
UserService
package com.spring.demo.demo.service;
import org.springframework.stereotype.Service;
@Service
public class UserService implements IUserService {
public String findUser(int id) {
return "用戶" + id;
}
}
在調用時,Controller會直接應用接口類,並添加@Autowired標籤。這裏的調用原理,即是Sping最著名的DI(依賴注入)和IoC(控制反轉)。
package com.spring.demo.demo.controller;
import com.spring.demo.demo.dto.UserCreateRequest;
...
@RestController
public class UserController {
@Autowired
IUserService userService;
@RequestMapping("/user/{id}")
public String find(@PathVariable int id) {
return userService.findUser(id);
}
@PostMapping("/user")
public void create(@RequestBody @Validated UserCreateRequest user) {
System.out.println(user.getName());
}
}
此時重啓Spring,並使用curl進行請求,可以得到結果:
$ curl -XGET 'http://127.0.0.1:8080/user/100'
$ 用戶100
Repository
Spring本身集成了Spring Data JPA,用來作爲訪問數據庫的ORM工具,它採用了Hibernate實現。在本教程,我們將實現User的增和查的工作。
添加依賴
修改pom.xml,添加對mysql和jpa的支持。
...
<dependencies>
...
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
服務配置
修改/resources/application.properties,添加相關的設置。
...
spring.datasource.url=jdbc:mysql://localhost:3306/demo?useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
...
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
spring.jpa.show-sql=true
添加Repository和Entity
Repository在DDD中是一個非常重要的概念,字面意思來講它就是一個倉庫。它屏蔽了SQL和數據庫操作的細節,使得業務代碼無需再考慮更細節的數據處理。它和Mybatis可以說是兩個完全相反的流派。
Spring Data JPA中,默認實現了Crud操作的Repository,可以直接繼承並使用這個框架進行快速的CRUD實現。
package com.spring.demo.demo.repo.repository;
import com.spring.demo.demo.repo.entity.User;
...
@Repository
public interface UserRepository extends CrudRepository<User, Integer> { }
對應數據庫中的數據表,會有一個實體Entity來定義它的表結構。
package com.spring.demo.demo.repo.entity;
import javax.persistence.*;
@Entity
@Table(name="t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private Integer gender;
// Getter & Setter
...
}
在Service調用中,即可簡單的使用save方法來保存新的User數據。
package com.spring.demo.demo.service;
import com.spring.demo.demo.repo.entity.User;
...
@Service
public class UserService implements IUserService {
@Autowired
UserRepository userRepo;
public String findUser(int id) {
return "用戶" + id;
}
public User createUser(User user) {
return userRepo.save(user);
}
}
最後在Controller中,修改Service方法並創建User。
package com.spring.demo.demo.controller;
import com.spring.demo.demo.dto.UserCreateRequest;
...
@RestController
public class UserController {
@Autowired
IUserService userService;
@RequestMapping("/user/{id}")
public String find(@PathVariable int id) {
return userService.findUser(id);
}
@PostMapping("/user")
public void create(@RequestBody @Validated UserCreateRequest userReq) {
User user = new User();
user.setName(userReq.getName());
user.setGender(userReq.getGender());
user = userService.createUser(user);
System.out.println("創建用戶 ID=" + user.getId() + " 用戶名=" + user.getName());
}
}
重啓Spring並提交創建請求。
$ curl -XPOST 'http://127.0.0.1:8080/user' -H 'Content-Type: application/json' -d'{"name":"用戶A","gender":0}'
可以看到,在console中有創建成功的提示信息。
Hibernate: insert into t_user (gender, name) values (?, ?)創建用戶 ID=1 用戶名=用戶A
緩存
對於不常改變的數據,常常需要進行緩存以提高系統性能和增加系統吞吐量。對此,Spring Cache提供了緩存的基本實現。
添加依賴
修改pom.xml,添加對cache的支持。
...
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
服務配置
修改/resources/application.properties,添加相關的設置。
...
spring.cache.type=simple
打開開關
需要在Application中,打開Cache開關。
package com.spring.demo.demo;
import org.springframework.boot.SpringApplication;
...
@SpringBootApplication
@EnableCaching
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
使用Cacheable標註
對於Cache內容,需要添加一個key名來保存內容。
package com.spring.demo.demo.service;
import com.spring.demo.demo.repo.entity.User;
...
@Service
public class UserService implements IUserService {
@Autowired
UserRepository userRepo;
@Cacheable(cacheNames = "user")
public User findUser(Integer id) {
System.out.println("取得用戶操作 ID=" + id);
return userRepo.findById(id).get();
}
public User createUser(User user) {
return userRepo.save(user);
}
}
重啓Spring,發送GET請求後可以看到,只有第一次執行了SQL操作,說明緩存處理已經完成。
$ curl -XGET 'http://127.0.0.1:8080/user/1'
$ 用戶A
$ 用戶A
> 取得用戶操作 ID=1=
> Hibernate: select user0_.id as id1_0_0_, user0_.gender as gender2_0_0_, user0_.name as name3_0_0_ from t_user user0_ where user0_.id=?
Redis
Spring的默認Cache處理並不使用Redis,要使用Redis作爲緩存應用,需要添加Redis的框架支持。
添加依賴
修改pom.xml,添加對cache的支持。
...
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
服務配置
修改/resources/application.properties,添加相關的設置。
...
spring.redis.host=127.0.0.1
spring.redis.password=123456
spring.redis.port=6379
spring.redis.jedis.pool.max-active=8
序列化對象
由於要保存到redis中,保存的實體對象需要進行序列化。
package com.spring.demo.demo.repo.entity;
import java.io.Serializable;
...
@Entity
@Table(name="t_user")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private Integer gender;
// Getter & Setter
...
}
重啓Spring,再試一遍上節的操作,仍然是一樣的結果,但緩存已經換爲redis。
$ curl -XGET 'http://127.0.0.1:8080/user/1'
$ 用戶A
$ 用戶A
> 取得用戶操作 ID=1
> Hibernate: select user0_.id as id1_0_0_, user0_.gender as gender2_0_0_, user0_.name as name3_0_0_ from t_user user0_ where user0_.id=?