【从零开始学Spring Boot】-3.Spring Boot WebMVC


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 特点

  1. 清晰的角色划分:控制器(controller)、验证器(validator)、命令对象(command obect)、表单对象(form object)、模型对象(model object)、Servlet分发器(DispatcherServlet)、处理器映射(handler mapping)、试图解析器(view resoler)等等。每一个角色都可以由一个专门的对象来实现。
  2. 强大而直接的配置方式:将框架类和应用程序类都能作为JavaBean配置,支持跨多个context的引用,例如,在web控制器中对业务对象和验证器validator)的引用。
  3. 可适配、非侵入:可以根据不同的应用场景,选择合适的控制器子类(simple型、command型、from型、wizard型、multi-action型或者自定义),而不是一个单一控制器(比如Action/ActionForm)继承。
  4. 可重用的业务代码:可以使用现有的业务对象作为命令或表单对象,而不需要去扩展某个特定框架的基类。
  5. 可定制的绑定(binding)和验证(validation):比如将类型不匹配作为应用级的验证错误,这可以保证错误的值。再比如本地化的日期和数字绑定等等。在其他某些框架中,你只能使用字符串表单对象,需要手动解析它并转换到业务对象。
  6. 可定制的handler mapping和view resolution:Spring提供从最简单的URL映射,到复杂的、专用的定制策略。与某些web MVC框架强制开发人员使用单一特定技术相比,Spring显得更加灵活。
  7. 灵活的model转换:在Springweb框架中,使用基于Map的键/值对来达到轻易的与各种视图技术集成。
  8. 可定制的本地化和主题(theme)解析:支持在JSP中可选择地使用Spring标签库、支持JSTL、支持Velocity(不需要额外的中间层)等等。
  9. 简单而强大的JSP标签库(Spring Tag Library):支持包括诸如数据绑定和主题(theme)之类的许多功能。他提供在标记方面的最大灵活性。
  10. JSP表单标签库:在Spring2.0中引入的表单标签库,使用在JSP编写表单更加容易。
  11. Spring Bean的生命周期可以被限制在当前的HTTp Request或者HTTp Session。准确的说,这并非Spring MVC框架本身特性,而应归属于Spring MVC使用的WebApplicationContext容器。

1.3 对比 WebFlux

2.环境

  1. JDK 1.8.0_201
  2. Spring Boot 2.2.0.RELEASE
  3. 构建工具(apache maven 3.6.3)
  4. 开发工具(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

5.参考

  1. SpringMVC的优点

文章作者: Soulballad
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Soulballad !
评论
 上一篇
【源码分析-Spring Boot】-3.Spring Boot WebMVC 工作流程及原理 【源码分析-Spring Boot】-3.Spring Boot WebMVC 工作流程及原理
Spring Boot WebMVC:【从零开始学Spring Boot】-3.Spring Boot WebMVC 1.Spring WebMvc 运行流程是怎样的? 用户发送请求至前端控制器 DispatcherServlet。
2020-07-12
下一篇 
【源码分析-Spring Boot】-2.Spring Boot 是如何解析配置注解的 【源码分析-Spring Boot】-2.Spring Boot 是如何解析配置注解的
Spring Boot 的配置解析: 【从零开始学Spring Boot】-2.Spring Boot ConfigurationProperties 配置 1.@ConfigurationProperties 是如何生效的? @Spr
2020-07-11
  目录