实际项目依赖的类库通常比较多,还要考虑它们之间的版本兼容。以前经常把常用依赖再包装成 xx-web、xx-db、xx-cache 之类的模块,使用时按需要引入,也方便统一升级。

Spring Boot 提供的 Starter 做的是类似的事情,只是粒度更适合常见开发场景。前面的 Spring Boot 之 Hello World 项目只引入了 spring-boot-starter-web,就同时得到了 Spring MVC、JSON 处理和内嵌 Tomcat 等依赖。

执行下面的命令可以查看 Starter 实际带进来的依赖:

mvn dependency:tree

Starter 不只是把几个 Jar 放到一起。配合 Spring Boot 的依赖管理,同一套版本中的组件已经做过统一搭配,项目通常不需要再给每个依赖单独写版本号。

接着前面的 Hello World 项目,使用 Web、Thymeleaf、Spring Data JPA 和 H2 做一个简单的用户新增与查询页面。

增加 Starter 依赖

在原来的 pom.xml 中使用下面这些依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

这里继续使用 Spring Boot 1.5.4.RELEASE 的 parent,因此这些依赖都不用再写版本号。

如果 Starter 中的某个传递依赖确定不需要,可以在依赖里排除。例如排除 jackson-databind:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </exclusion>
    </exclusions>
</dependency>

排除依赖不是必须步骤,确认项目确实不使用它时再做。

编写实体类

创建 com.example.demo.domain.User:

package com.example.demo.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "app_user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;
    private String mobile;
    private Integer age;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

编写 Repository

创建 com.example.demo.dao.UserRepository:

package com.example.demo.dao;

import com.example.demo.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByMobileEndingWith(String mobile);
}

JpaRepository 已经提供保存、删除和常用查询方法,不需要自己写实现类。findByMobileEndingWith 会根据方法名生成按手机号尾数查询的条件。

编写 Controller

创建 com.example.demo.web.UserController:

package com.example.demo.web;

import com.example.demo.dao.UserRepository;
import com.example.demo.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.annotation.Resource;
import java.util.List;

@Controller
public class UserController {
    @Resource
    private UserRepository userRepository;

    @RequestMapping("/userList")
    public String userList(String mobile, Model model) {
        List<User> users = StringUtils.hasText(mobile)
                ? userRepository.findByMobileEndingWith(mobile)
                : userRepository.findAll();
        model.addAttribute("users", users);
        return "userList";
    }

    @RequestMapping(value = "/addUser", method = RequestMethod.POST)
    public String addUser(User user) {
        userRepository.save(user);
        return "redirect:/userList?mobile=" + user.getMobile();
    }
}

这里增加了两个入口:userList 显示全部用户或按手机号尾数查询,addUser 保存用户后再跳回列表页。

编写页面模板

在 src/main/resources/templates 下创建 userList.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>User List</title>
</head>
<body>
<ul>
    <li th:each="user : ${users}">
        <span th:text="${user.id}"></span>
        <span th:text="${user.name}"></span>
        <span th:text="${user.mobile}"></span>
        <span th:text="${user.age}"></span>
    </li>
</ul>

<form method="post" th:action="@{/addUser}">
    <label>Name:</label>
    <input name="name" size="10" /><br />
    <label>Mobile:</label>
    <input name="mobile" size="11" /><br />
    <label>Age:</label>
    <input name="age" size="3" /><br />
    <input type="submit" value="Save" />
</form>
</body>
</html>

页面上面显示用户列表,下面提交新用户。

运行项目

启动项目后访问:

http://127.0.0.1:8080/userList

输入姓名、手机号和年龄并提交,程序会保存用户并跳回列表页,刚添加的数据会显示在页面上方。

这个项目依然没有数据库连接配置。因为 H2 是内存数据库,Spring Boot 检测到它以后会自动配置数据源,并根据实体创建表;应用停止后,数据也会随之消失。

Starter 解决的是依赖组合和版本管理,真正让数据源、JPA、Thymeleaf 等组件自动工作的是 Spring Boot 的自动配置。两者经常一起出现,但不是同一个概念。相关行为可以对照 Spring Boot 1.5.4 参考文档 和 Spring Data JPA 1.11.4 文档。

最后修改:2017 年 08 月 24 日
如果觉得我的文章对你有用,请随意赞赏