乐趣区

Spring-Boot一快速开始

Spring Boot(一):快速开始

本系列文章旨在使用最小依赖、最简单配置,帮助初学者快速掌握 Spring Boot 各组件使用,达到快速入门的目的。全部文章所使用示例代码均同步 Github 仓库和 Gitee 仓库。

1. Spring Boot 是什么?

Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。

讲的通俗一点就是 Spring Boot 并不是一个新的框架,它只是整合和默认实现了很多框架的配置方式。

2. 好处是什么?

最大的好处就是简单、快捷、方便,在 Spring Boot 之前,我们如果要搭建一个框架需要做什么?

  • 配置 web.xml,加载 Spring 和 Spring MVC,加载各种过滤器、拦截器
  • 在配置文件 application.xml 中配置数据库、配置缓存、配置连接池等等
  • 配置日志文件
  • 配置各种配置文件的读取
  • 配置上下文、配置定时任务
  • 各种各样的配置

笔者手边正好有一个很久之前的项目,当时还是使用的 Spring3.x,可以给各位看一下当时一个项目的配置文件有多少:

而我如果需要新建一个项目,这里面大量的配置文件都要 copy 过去,并且重新调试,非常的不方便且浪费时间,当 Spring Boot 横空出世的时候,这些噩梦都结束了。

Spring Boot 的优势:

  • 为所有 Spring 开发者更快的入门
  • 开箱即用,提供各种默认配置来简化项目配置
  • 内嵌式容器简化 Web 项目
  • 没有冗余代码生成和 XML 配置的要求

3. 快速入门

目标设定:构建一个简单的 RESTful API 并实现对应的单元测试

3.1 工程构建方式

Spring Boot 提供两种工程构建方式:

关于创建 springcloud 项目,目前有两种比较方便的方案,核心都是一样的,大家自行选择自己使用方便的。

方式一:

打开 spring 的官方链接:

https://start.spring.io/

在 Group 中填入自己的组织,一般填写公司的域名的到写,例如 com.jd 或者 com
.baidu,这里我直接写 com.springboot

在 Artifact 中填写工程的名称,这里我直接写 spring-boot-quick-start。

package 选择 jar,java 选择 11(目前最新的 LTS 版本),至此,基础选择已经全都选完,接下来要开始选择我们使用的 Spring Boot 的组件了。

在 Dependencies 中找到 Spring Web,选择 Spring Web,结果如下图:

最后点击下方的绿色长条按钮 Generate the project 进行下载,等待下载完成后,直接将压缩包解压导入我们的编辑工具 idea 里即可。

方式二:

基于 idea 创建,打开 idea,首先 file->new->project,选中 Spring Initializr,这时可以看到右侧让我们选择一个初始化的服务 url,默认的就是上面的官方链接,https://start.spring.io/

点击 next 下一步,填写和上面一样的 Group、Artifact、java 版本、package 方式等信息,继续 next 下一步,选择依赖,和前面的方法的一样,在 Dependencies 中找到 Spring Web,选择 Spring Web,点击 next,选择项目名称和存储路径,点击 finish,静静等一会,第一个项目 spring-boot-quick-start 就新鲜出炉了~~~

我一般选择第一种方式创建 Spring Boot 项目,这种方式不依赖 IDE 工具。

3.2 工程结构解析

首先先看一下我们创建的工程结构,如下图:

  • pom.xml:maven 工程配置文件,主要配置当前工程的一些基本信息,包含我们当前使用的组件,版本等信息。
  • src/main/java 下的程序入口:Chapter1Application。
  • src/main/resources 下的配置文件:application.properties。
  • src/test/ 下的测试入口:Chapter1ApplicationTests。

3.3 pom.xml

这里我们重点关注 <dependencies> 标签,这里写明了我们引入的组件

<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>
  • spring-boot-starter-web:Web 模块
  • spring-boot-starter-test:测试模块,包括 JUnit、Hamcrest、Mockito

3.4 使用 Spring MVC 实现一组对 User 对象的 RESTful API

RESTful API 设计如下:

请求类型 URL 功能
GET / 查询用户列表
POST / 创建 User
GET /{id} 根据 url 中的 id 获取 user 信息
PUT /{id} 根据 id 更新用户信息
DELETE /{id} 根据 id 删除用户信息

注意:RESTful 接口在设计的时候应该遵循标准的方法以及语义,这些语义包含了安全性和幂等性等方面的考量,例如 GET 和 HEAD 请求都是安全的,无论请求多少次,都不会改变服务器状态。而 GET、HEAD、PUT 和 DELETE 请求都是幂等的,无论对资源操作多少次,结果总是一样的,后面的请求并不会产生比第一次更多的影响。

下面列出了 GET,DELETE,PUT 和 POST 的典型用法:

GET

  • 安全且幂等
  • 获取表示
  • 变更时获取表示(缓存)

POST

  • 不安全且不幂等
  • 使用服务端管理的(自动产生)的实例号创建资源
  • 创建子资源
  • 部分更新资源
  • 如果没有被修改,则不过更新资源(乐观锁)

PUT

  • 不安全但幂等
  • 用客户端管理的实例号创建一个资源
  • 通过替换的方式更新资源
  • 如果未被修改,则更新资源(乐观锁)

DELETE

  • 不安全但幂等
  • 删除资源

用户 Model 类如下:

public class UserModel {
    private Long id;
    private String name;
    private int age;
    
    // 省略 getter 和 setter
}

REST API 实现类如下:

@RestController
public class UserController {

    // 创建线程安全的 Map,用作数据存储
    static Map<Long, UserModel> users = new ConcurrentHashMap<>();

    /**
     * 查询用户列表
     * @return
     */
    @GetMapping("/")
    public List<UserModel> getUserList() {List<UserModel> list = new ArrayList<UserModel>(users.values());
        return list;
    }

    /**
     * 创建 User
     * @param userModel
     * @return
     */
    @PostMapping("/")
    public UserModel postUser(@ModelAttribute UserModel userModel) {users.put(userModel.getId(), userModel);
        return users.get(userModel.getId());
    }

    /**
     * {id} 根据 url 中的 id 获取 user 信息
     * url 中的 id 可通过 @PathVariable 绑定到函数的参数中
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public UserModel getUser(@PathVariable Long id) {return users.get(id);
    }

    /**
     * 根据 id 更新用户信息
     * @param id
     * @param userModel
     * @return
     */
    @PutMapping("/{id}")
    public UserModel putUser(@PathVariable Long id, @ModelAttribute UserModel userModel) {UserModel u = users.get(id);
        u.setName(userModel.getName());
        u.setAge(userModel.getAge());
        users.put(id, u);
        return users.get(userModel.getId());
    }

    /**
     * 根据 id 删除用户信息
     * @param id
     * @return
     */
    @DeleteMapping("/{id}")
    public String deleteUser(@PathVariable Long id) {users.remove(id);
        return "success";
    }
}
  • @Controller:修饰 class,用来创建处理 http 请求的对象
  • @RestController:Spring4 之后加入的注解,原来在 @Controller 中返回 json 需要 @ResponseBody 来配合,如果直接用 @RestController 替代 @Controller 就不需要再配置 @ResponseBody,默认返回 json 格式。

可以看一下 @RestController,可以看到 @RestController 本身就是由 @ResponseBody@Controller 组成的,源码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {

    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any (or empty String otherwise)
     * @since 4.0.1
     */
    @AliasFor(annotation = Controller.class)
    String value() default "";}

单元测试类如下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootQuickStartApplicationTests {

    private MockMvc mvc;

    @Before
    public void setUp() throws Exception {mvc = MockMvcBuilders.standaloneSetup(new UserController()).build();}

    @Test
    public void contextLoads() throws Exception {
        RequestBuilder request = null;

        // 1、get 查一下 user 列表,应该为空
        request = MockMvcRequestBuilders.get("/")
                .contentType(MediaType.APPLICATION_JSON);
        mvc.perform(request)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        // 2、post 提交一个 user
        request = MockMvcRequestBuilders.post("/")
                .param("id", "1")
                .param("name", "Spring Boot")
                .param("age", "18")
                .contentType(MediaType.APPLICATION_JSON);
        mvc.perform(request)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();


        // 3、get 获取 user 列表,应该有刚才插入的数据
        request = MockMvcRequestBuilders.get("/")
                .contentType(MediaType.APPLICATION_JSON);
        mvc.perform(request)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        // 4、put 修改 id 为 1 的 user
        request = MockMvcRequestBuilders.put("/1")
                .param("name", "Spring Boot Test")
                .contentType(MediaType.APPLICATION_JSON);

        mvc.perform(request)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        // 5、get 一个 id 为 1 的 user
        request = MockMvcRequestBuilders.get("/1")
                .contentType(MediaType.APPLICATION_JSON);

        mvc.perform(request)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        // 6、del 删除 id 为 1 的 user
        request = MockMvcRequestBuilders.delete("/1")
                .contentType(MediaType.APPLICATION_JSON);

        mvc.perform(request)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        // 7、get 查一下 user 列表,应该为空

        request = MockMvcRequestBuilders.get("/")
                .contentType(MediaType.APPLICATION_JSON);

        mvc.perform(request)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();}

}

启动测试类,控制台打印如下,这里仅截取一段内容做展示:

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /
       Parameters = {id=[1], name=[Spring Boot], age=[18]}
          Headers = [Content-Type:"application/json"]
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.springboot.springbootquickstart.controller.UserController
           Method = public com.springboot.springbootquickstart.model.UserModel com.springboot.springbootquickstart.controller.UserController.postUser(com.springboot.springbootquickstart.model.UserModel)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json;charset=UTF-8"]
     Content type = application/json;charset=UTF-8
             Body = {"id":1,"name":"Spring Boot","age":18}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

从控制台打印中可以完整的看到整个模拟请求的过程以及参数。

示例代码 -Github
示例代码 -Gitee

4. 参考

《Spring Boot(一):入门篇》
《Spring Boot 构建 RESTful API 与单元测试》

退出移动版