1.简介
1.1 概述
The Spring portfolio provides two parallel stacks. One is based on a Servlet API with Spring MVC and Spring Data constructs. The other is a fully reactive stack that takes advantage of Spring WebFlux and Spring Data’s reactive repositories. In both cases, Spring Security has you covered with native support for both stacks. https://spring.io/reactive
Spring产品组合提供了两个并行技术栈。一种基于带有 Spring MVC 和 Spring Data 结构的 Servlet API。另一个是完全响应式技术栈,该栈利用了 Spring WebFlux 和 Spring Data 的响应式存储库。在这两种情况下,Spring Security 都为两个堆栈提供了本机支持。
1.2 特点
- 清晰的角色划分:控制器(controller)、验证器(validator)、命令对象(command obect)、表单对象(form object)、模型对象(model object)、Servlet分发器(DispatcherServlet)、处理器映射(handler mapping)、试图解析器(view resoler)等等。每一个角色都可以由一个专门的对象来实现。
- 强大而直接的配置方式:将框架类和应用程序类都能作为JavaBean配置,支持跨多个context的引用,例如,在web控制器中对业务对象和验证器validator)的引用。
- 可适配、非侵入:可以根据不同的应用场景,选择合适的控制器子类(simple型、command型、from型、wizard型、multi-action型或者自定义),而不是一个单一控制器(比如Action/ActionForm)继承。
- 可重用的业务代码:可以使用现有的业务对象作为命令或表单对象,而不需要去扩展某个特定框架的基类。
- 可定制的绑定(binding)和验证(validation):比如将类型不匹配作为应用级的验证错误,这可以保证错误的值。再比如本地化的日期和数字绑定等等。在其他某些框架中,你只能使用字符串表单对象,需要手动解析它并转换到业务对象。
- 可定制的handler mapping和view resolution:Spring提供从最简单的URL映射,到复杂的、专用的定制策略。与某些web MVC框架强制开发人员使用单一特定技术相比,Spring显得更加灵活。
- 灵活的model转换:在Springweb框架中,使用基于Map的键/值对来达到轻易的与各种视图技术集成。
- 可定制的本地化和主题(theme)解析:支持在JSP中可选择地使用Spring标签库、支持JSTL、支持Velocity(不需要额外的中间层)等等。
- 简单而强大的JSP标签库(Spring Tag Library):支持包括诸如数据绑定和主题(theme)之类的许多功能。他提供在标记方面的最大灵活性。
- JSP表单标签库:在Spring2.0中引入的表单标签库,使用在JSP编写表单更加容易。
- Spring Bean的生命周期可以被限制在当前的HTTp Request或者HTTp Session。准确的说,这并非Spring MVC框架本身特性,而应归属于Spring MVC使用的WebApplicationContext容器。
1.3 对比 WebFlux
2.环境
- JDK 1.8.0_201
- Spring Boot 2.2.0.RELEASE
- 构建工具(apache maven 3.6.3)
- 开发工具(IntelliJ IDEA )
3.代码
3.1 代码结构
3.2 maven 依赖
<dependencies>
<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>
3.3 java代码
User.java
public class User {
/**
* id
*/
private Integer id;
/**
* 姓名
*/
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public User() {
}
}
UserRepository.java
@Repository
public class UserRepository {
// 生成id
private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
// 模拟内存数据库
private static final Map<Integer, User> USER_MAP = new HashMap<>();
public List<User> selectAll() {
return new ArrayList<>(USER_MAP.values());
}
public User getUserById(Integer id) {
return USER_MAP.get(id);
}
public User addUser(User user) {
if (Objects.isNull(user.getId())) {
user.setId(ID_GENERATOR.incrementAndGet());
}
USER_MAP.put(user.getId(), user);
return user;
}
public User update(User user) {
USER_MAP.put(user.getId(), user);
return user;
}
public User delete(Integer id) {
return USER_MAP.remove(id);
}
public boolean exist(User user) {
List<String> nameList = USER_MAP.values().stream().map(User::getName).collect(Collectors.toList());
return nameList.contains(user.getName());
}
}
UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
public List<User> selectAll() {
return userRepository.selectAll();
}
@Override
public User getUserById(Integer id) {
return userRepository.getUserById(id);
}
@Override
public User addUser(User user) {
return userRepository.addUser(user);
}
@Override
public User update(User user) {
return userRepository.update(user);
}
@Override
public User delete(Integer id) {
return userRepository.delete(id);
}
@Override
public boolean exist(User user) {
return userRepository.exist(user);
}
}
UserController.java
@RestController
@RequestMapping(value = "/user")
public class UserController {
private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class);
@Autowired
private UserService userService;
@RequestMapping(value = "/list", method = RequestMethod.GET)
public List<User> list() {
return userService.selectAll();
}
@RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
public User get(@PathVariable Integer id) {
return userService.getUserById(id);
}
@RequestMapping(value = "/add", method = RequestMethod.POST)
public ResponseEntity<Void> create(@RequestBody User user, UriComponentsBuilder builder) {
if ("duplicated".equals(user.getName())) {
LOGGER.warn("the user already exist");
return new ResponseEntity<>(HttpStatus.ALREADY_REPORTED);
}
userService.addUser(user);
HttpHeaders headers = new HttpHeaders();
headers.setLocation(builder.path("/user/get/{id}").buildAndExpand(user.getId()).toUri());
return new ResponseEntity<>(headers, HttpStatus.CREATED);
}
@RequestMapping(value = "/update", method = RequestMethod.PUT)
public User update(@RequestBody User user) {
return userService.update(user);
}
@RequestMapping(value = "/delete/{id}", method = RequestMethod.DELETE)
public User delete(@PathVariable Integer id) {
return userService.delete(id);
}
}
3.4 git 地址
spring-boot/spring-boot-03-webmvc
4.效果展示
启动 SpringBoot03WebApplication.main 方法,在 spring-boot-03-webmvc.http 访问下列地址,观察输出信息是否符合预期。
### GET /user/list
GET http://localhost:8080/user/list
由于数据保存在内存中,最开始没有数据,所以返回为空。可以调用 /add 添加数据后再查询
### GET /user/get/{id}
GET http://localhost:8080/user/get/1
通过 id 查询同样为空,可以调用 /add 添加数据后再查询
### POST /user/add
POST http://localhost:8080/user/add
Content-Type: application/json
{
"name": "zhangsan"
}
这里响应码为 201,同时响应头中 location 设定为一个新的地址
### PUT /user/update
PUT http://localhost:8080/user/update
Content-Type: application/json
Accept: application/json
{
"id": 1,
"name": "lisi"
}
### DELETE /user/delete/{id}
DELETE http://localhost:8080/user/delete/1