环境准备
- 系统: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=?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。