现在,我们里利用了整本书的篇幅来介绍Spring。尽管Spring带来的主要益处就是简化Java开发,但本章将会介绍Spring Boot如何让这项任务变得更简单。
Spring Boot在Spring之上,构建了全新的开发模式,移除了开发Spring应用中很多单调乏味的东西。
我们首先整体了解一下Spring Boot,看它是怎么简化Spring的。
在Spring家族中,Spring Boot主要提供了四个特性,能够改变Spring应用程序的方式:
添加Starter依赖
有两种制作蛋糕的方式:一种是买好面粉、鸡蛋、糖、发酵粉、奶油等等,自己搅拌成糊状制作;另一种是购买打包好的蛋糕,我们只需要加入一些其他的辅料即可。
Spring boot就是后面一种方法。
Spring Boot Starter 依赖将所需的常见依赖按组聚集在一起,形成单条依赖。
自动配置
Spring boot的Starter减少了pom.xml中依赖的长度。而Spring boot的自动配置功能则能削减Spring配置的数量。它在实现时,会考虑应用中的其他因素并推断你所需要的Spring配置。
CLI
Spring boot CLI充分利用了Spring boot starter和自动配置的魔力,并添加了一些Groovy的功能。它简化了Spring的开发流程,通过CLI,我们能够运行一个或多个Groovy脚本,并查看它是如何运行的。在应用的运行过程中,CLI能够自动导入Spring类型并解析依赖。
用来阐述Spring Boot CLI最有趣的例子就是如下的Groovy脚本:
@RestController
class Hi{
@RequestMapping("/")
String hi(){
"Hi!"
}
}
很难相信这是一个完整的Spring应用吧?但是它的确是对的。如果你已经安装过Spring Boot CLI,我们可以使用如下的命令行来运行它:
$ spring run Hi.groovy
Actuator
Spring Boot Actuator为Spring Boot项目带来很多游泳的特性,包括:
之后我们会介绍到。接下来,我们将使用以上4个主要特性构建一个微小但完整的应用程序。
我们的应用程序是一个简单的联系人列表,它允许用户输入联系人信息(名字、电话、EMail),并且能够列出用户之前输入的所有联系人信息。
你可以使用Mvn或者Gradle来构建。
Gradle:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.1.4.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'spring-boot'
//构建Jar文件
jar {
baseName = 'contacts'
version = '0.1.0'
}
repositories {
mavenCentral()
}
//依赖
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-jdbc")
compile("org.thymeleaf:thymeleaf-spring4")
compile("com.h2database:h2")
}
task wrapper(type: Wrapper) {
gradleVersion = '2.0'
}
Maven
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.habuma</groupId>
<artifactId>contacts</artifactId>
<version>0.1.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.1.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Gradle的好处就在于精简,往往Maven三四行的代码,Gradle一行就能概括。比如maven里面的spring-boot插件,使用
,在Gradle里面使用一行:apply plugin: 'spring-boot'
。
处理请求
因为我们要使用SpringMVC来开发应用Web层,因此需要将Spring Mvc作为依赖添加到构建中。我们已经讨论过,Spring Boot的Web Starter能够将Spring MVC需要的所有内容一站式添加到构建中。
gradle:
compile("org.springframework.boot:spring-boot-starter-web")
maven:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
注意为什么这里没有版本号呢?因为Spring Boot parent项目已经制定了版本。
现在我们编写控制器类:
package com.promusician;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/")
public class ContactController {
private ContactRepository contactRepo;
@Autowired
public ContactController(ContactRepository contactRepo) {
this.contactRepo = contactRepo;
}
@RequestMapping(method=RequestMethod.GET)
public String home(Map<String,Object> model) {
List<Contact> contacts = contactRepo.findAll();
model.put("contacts", contacts);
return "home";
}
@RequestMapping(method=RequestMethod.POST)
public String submit(Contact contact) {
contactRepo.save(contact);
return "redirect:/";
}
}
有了前面的基础,这个程序很好理解,不做赘述。
按照传统方式,Java Web应用会使用JSP作为视图层的技术。但是,正如在第六章所说(不好意思我跳过了)。在这个领域有一个新的参与者。Thymeleaf的原生模版比JSP更加便于使用,而且它能够让我们以HTML的形式编写模版。鉴于此,我们将使用Thymeleaf来定义“home”视图。
<dependency>
<groupId>org.thymeleafgroupId>
<artifactId>thymeleaf-spring4artifactId>
dependency>
或者import("org.thymeleaf:thymeleaf-spring4")
需要记住的是,只要我们将Thymeleaf添加到项目的类路径下,就启用了Spring Boot的自动配置。当应用运行时,Spring Boot将会探测到类路径中的Thymeleaf,然后会自动配置视图解析器、模版解析器以及模版引擎。因此,在我们的应用中,不需要显示Spring配置的方法来定义Thymeleaf。
现在来定义视图模版:
如下是home.html:
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Boot Contactstitle>
<link rel="stylesheet" th:href="@{/style.css}" />
head>
<body>
<h2>Spring Boot Contactsh2>
<form method="POST">
<label for="firstName">First Name:label>
<input type="text" name="firstName">input><br/>
<label for="lastName">Last Name:label>
<input type="text" name="lastName">input><br/>
<label for="phoneNumber">Phone #:label>
<input type="text" name="phoneNumber">input><br/>
<label for="emailAddress">Email:label>
<input type="text" name="emailAddress">input><br/>
<input type="submit">input>
form>
<ul th:each="contact : ${contacts}">
<li>
<span th:text="${contact.firstName}">Firstspan>
<span th:text="${contact.lastName}">Lastspan> :
<span th:text="${contact.phoneNumber}">phoneNumberspan>,
<span th:text="${contact.emailAddress}">emailAddressspan>
li>
ul>
body>
html>
因为ContactController中home()方法所返回的逻辑视图名为home,因此模版应该放在指定位置:"src/main/resources/templates"中。
持久化数据
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>com.h2databasegroupId>
<artifactId>h2artifactId>
dependency>
或者gradle略。
现在编写Repository类:
package com.promusician;
import java.util.List;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
@Repository
public class ContactRepository {
private JdbcTemplate jdbc;
@Autowired
public ContactRepository(JdbcTemplate jdbc) {
this.jdbc = jdbc;
}
public List<Contact> findAll() {
return jdbc.query(
"select id, firstName, lastName, phoneNumber, emailAddress " +
"from contacts order by lastName",
new RowMapper<Contact>() {
public Contact mapRow(ResultSet rs, int rowNum) throws SQLException {
Contact contact = new Contact();
contact.setId(rs.getLong(1));
contact.setFirstName(rs.getString(2));
contact.setLastName(rs.getString(3));
contact.setPhoneNumber(rs.getString(4));
contact.setEmailAddress(rs.getString(5));
return contact;
}
}
);
}
public void save(Contact contact) {
jdbc.update(
"insert into contacts " +
"(firstName, lastName, phoneNumber, emailAddress) " +
"values (?, ?, ?, ?)",
contact.getFirstName(), contact.getLastName(),
contact.getPhoneNumber(), contact.getEmailAddress());
}
}
我们仔细看,这个Repository类非常简单,简单的增删改查。不过JdbcTemplate呢????我们为啥不用去声明一个DataSource,然后在声明一个JdbcTemplate?
这种问题的回答就是,自动配置!
那数据库模式该怎么处理呢?我们必须自己定义contacts表的模式,因为Spring Boot再强大也没法猜测contacts表的样子。于是,我们这样设置:
create table contacts (
id identity,
firstName varchar(30) not null,
lastName varchar(50) not null,
phoneNumber varchar(13),
emailAddress varchar(30)
);
现在我们只需要一种方式加载这个"create table"的SQL并将其在H2数据库中执行就行了。
尝试运行
最后不需要配置DispatcherServlet,启用自动配置就行了。
package com.promusician;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan
@EnableAutoConfiguration
public class PromusicianApplication {
public static void main(String[] args) {
SpringApplication.run(PromusicianApplication.class, args);
}
}
本书结束,我们换本书。