现在,我们里利用了整本书的篇幅来介绍Spring。尽管Spring带来的主要益处就是简化Java开发,但本章将会介绍Spring Boot如何让这项任务变得更简单。 Spring Boot在Spring之上,构建了全新的开发模式,移除了开发Spring应用中很多单调乏味的东西。 我们首先整体了解一下Spring Boot,看它是怎么简化Spring的。
在Spring家族中,Spring Boot主要提供了四个特性,能够改变Spring应用程序的方式:
Spring Boot Starter:它将常用的依赖分组进行了整合,将其合并到一个依赖中,这样就可以一次性添加到项目的Mvn或Gradle构建中。自动配置:Spring Boot的自动配置特性利用了Spring4对条件化配置的支持,合理地推测应用所需的bean并自动化配置它们。命令行接口(Command-line interface,CLI):CLI发挥了Groovy编程语言的优势,并结合自动配置进一步简化Spring应用的开发。Actuator:它为SpringBoot应用添加了一定的管理特性。添加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项目带来很多游泳的特性,包括:
管理端点合理的异常处理以及默认的“/error”映射端点获取应用信息的“/info”端点当启用Spring Security时,会有一个审计事件框架之后我们会介绍到。接下来,我们将使用以上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插件,使用<plugin>,在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.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </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.thymeleaf</groupId> <artifactId>thymeleaf-spring4</artifactId> </dependency>或者import("org.thymeleaf:thymeleaf-spring4") 需要记住的是,只要我们将Thymeleaf添加到项目的类路径下,就启用了Spring Boot的自动配置。当应用运行时,Spring Boot将会探测到类路径中的Thymeleaf,然后会自动配置视图解析器、模版解析器以及模版引擎。因此,在我们的应用中,不需要显示Spring配置的方法来定义Thymeleaf。
现在来定义视图模版: 如下是home.html:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Spring Boot Contacts</title> <link rel="stylesheet" th:href="@{/style.css}" /> </head> <body> <h2>Spring Boot Contacts</h2> <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}">First</span> <span th:text="${contact.lastName}">Last</span> : <span th:text="${contact.phoneNumber}">phoneNumber</span>, <span th:text="${contact.emailAddress}">emailAddress</span> </li> </ul> </body> </html>因为ContactController中home()方法所返回的逻辑视图名为home,因此模版应该放在指定位置:"src/main/resources/templates"中。
持久化数据
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </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); } }本书结束,我们换本书。