实际项目依赖的类库通常比较多,还要考虑它们之间的版本兼容。以前经常把常用依赖再包装成 xx-web、xx-db、xx-cache 之类的模块,使用时按需要引入,也方便统一升级。
Spring Boot 提供的 Starter 做的是类似的事情,只是粒度更适合常见开发场景。前面的 Spring Boot 之 Hello World 项目只引入了 spring-boot-starter-web,就同时得到了 Spring MVC、JSON 处理和内嵌 Tomcat 等依赖。
执行下面的命令可以查看 Starter 实际带进来的依赖:
mvn dependency:treeStarter 不只是把几个 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 文档。