1.简介
如果您一直想使用一个Web框架,它使您能够快速开始API开发,而无须设置Web服务器,收集所有有线依赖项,安装各种工具的麻烦,那么您将拥有一个出色的框架,即Spring开机 Spring Boot的主要座右铭是约定优于配置 。
在本课程中,我们将研究设置Spring Boot项目并提供一些RESTful服务在不到20分钟的时间内与数据库进行交互的难易程度! 确实,Spring Boot就是这么简单。 Spring Boot使我们能够制作可以“运行”的基于生产级的基于Spring的应用程序。 Spring Boot应用程序最好的部分是它只需要很少的配置。
2.使用Maven制作Spring Boot项目
我们将使用许多Maven原型之一为我们的示例创建一个示例项目。 要创建项目,请在将用作工作空间的目录中执行以下命令:
mvn archetype:generate -DgroupId=com.javacodegeeks.example -DartifactId=JCG-SpringBoot-example -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
如果您是第一次运行maven,则完成生成命令将花费几秒钟,因为maven必须下载所有必需的插件和工件才能完成生成任务。
创建项目后,请随时在您喜欢的IDE中打开它。 下一步是向项目添加适当的Maven依赖关系。 我们将在项目中处理四个依赖项:
-
spring-boot-starter-web
:此依赖关系将该项目标记为Web项目,并且添加了依赖关系以创建控制器并创建与Web相关的类。 -
spring-boot-starter-data-jpa
:此依赖关系为项目提供了JPA支持,我们可以在其中执行许多与数据库相关的操作 -
h2
:H2是一个内存数据库,在本课中我们将使用它来演示数据库操作 -
spring-boot-starter-test
:此依赖项将所有与测试相关的JAR收集到项目中,例如JUnit和Mockito 。
这是带有适当依赖项的pom.xml
文件:
pom.xml
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.10.RELEASE</version><relativePath/> <!-- lookup parent from repository -->
</parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version>
</properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
要将代码打包到JAR中,我们将使用spring-boot-maven-plugin
,它是管理应用程序自身打包到JAR或WAR中的出色工具。 这是我们可以添加到依赖文件中的方法:
pom.xml
<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins>
</build>
在Maven Central上找到最新的Maven依赖项。
最后,要了解添加此依赖项时添加到项目中的所有JAR,我们可以运行一个简单的Maven命令,当我们向其添加一些依赖项时,该命令使我们能够查看项目的完整依赖关系树。 这是我们可以使用的命令:
检查依赖树
mvn dependency:tree
当我们运行此命令时,它将向我们显示以下依赖关系树:
依赖树
[INFO] --------------------
[INFO] Building JCG-SpringBoot-example 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.10:tree (default-cli) @ JCG-SpringBoot-example ---
[INFO] com.javacodegeeks.example:JCG-SpringBoot-example:jar:1.0-SNAPSHOT
[INFO] +- org.springframework.boot:spring-boot-starter-data-jpa:jar:1.5.10.RELEASE:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:1.5.10.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot:jar:1.5.10.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:1.5.10.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:1.5.10.RELEASE:compile
[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.1.11:compile
[INFO] | | | | \- ch.qos.logback:logback-core:jar:1.1.11:compile
[INFO] | | | +- org.slf4j:jul-to-slf4j:jar:1.7.25:compile
[INFO] | | | \- org.slf4j:log4j-over-slf4j:jar:1.7.25:compile
[INFO] | | \- org.yaml:snakeyaml:jar:1.17:runtime
[INFO] | +- org.springframework.boot:spring-boot-starter-aop:jar:1.5.10.RELEASE:compile
[INFO] | | +- org.springframework:spring-aop:jar:4.3.14.RELEASE:compile
[INFO] | | \- org.aspectj:aspectjweaver:jar:1.8.13:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-jdbc:jar:1.5.10.RELEASE:compile
[INFO] | | +- org.apache.tomcat:tomcat-jdbc:jar:8.5.27:compile
[INFO] | | | \- org.apache.tomcat:tomcat-juli:jar:8.5.27:compile
[INFO] | | \- org.springframework:spring-jdbc:jar:4.3.14.RELEASE:compile
[INFO] | +- org.hibernate:hibernate-core:jar:5.0.12.Final:compile
[INFO] | | +- org.jboss.logging:jboss-logging:jar:3.3.1.Final:compile
[INFO] | | +- org.hibernate.javax.persistence:hibernate-jpa-2.1-api:jar:1.0.0.Final:compile
[INFO] | | +- org.javassist:javassist:jar:3.21.0-GA:compile
[INFO] | | +- antlr:antlr:jar:2.7.7:compile
[INFO] | | +- org.jboss:jandex:jar:2.0.0.Final:compile
[INFO] | | +- dom4j:dom4j:jar:1.6.1:compile
[INFO] | | \- org.hibernate.common:hibernate-commons-annotations:jar:5.0.1.Final:compile
[INFO] | +- org.hibernate:hibernate-entitymanager:jar:5.0.12.Final:compile
[INFO] | +- javax.transaction:javax.transaction-api:jar:1.2:compile
[INFO] | +- org.springframework.data:spring-data-jpa:jar:1.11.10.RELEASE:compile
[INFO] | | +- org.springframework.data:spring-data-commons:jar:1.13.10.RELEASE:compile
[INFO] | | +- org.springframework:spring-orm:jar:4.3.14.RELEASE:compile
[INFO] | | +- org.springframework:spring-context:jar:4.3.14.RELEASE:compile
[INFO] | | +- org.springframework:spring-tx:jar:4.3.14.RELEASE:compile
[INFO] | | +- org.springframework:spring-beans:jar:4.3.14.RELEASE:compile
[INFO] | | +- org.slf4j:slf4j-api:jar:1.7.25:compile
[INFO] | | \- org.slf4j:jcl-over-slf4j:jar:1.7.25:compile
[INFO] | \- org.springframework:spring-aspects:jar:4.3.14.RELEASE:compile
[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:1.5.10.RELEASE:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-tomcat:jar:1.5.10.RELEASE:compile
[INFO] | | +- org.apache.tomcat.embed:tomcat-embed-core:jar:8.5.27:compile
[INFO] | | | \- org.apache.tomcat:tomcat-annotations-api:jar:8.5.27:compile
[INFO] | | +- org.apache.tomcat.embed:tomcat-embed-el:jar:8.5.27:compile
[INFO] | | \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:8.5.27:compile
[INFO] | +- org.hibernate:hibernate-validator:jar:5.3.6.Final:compile
[INFO] | | +- javax.validation:validation-api:jar:1.1.0.Final:compile
[INFO] | | \- com.fasterxml:classmate:jar:1.3.4:compile
[INFO] | +- com.fasterxml.jackson.core:jackson-databind:jar:2.8.10:compile
[INFO] | | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.8.0:compile
[INFO] | | \- com.fasterxml.jackson.core:jackson-core:jar:2.8.10:compile
[INFO] | +- org.springframework:spring-web:jar:4.3.14.RELEASE:compile
[INFO] | \- org.springframework:spring-webmvc:jar:4.3.14.RELEASE:compile
[INFO] | \- org.springframework:spring-expression:jar:4.3.14.RELEASE:compile
[INFO] +- com.h2database:h2:jar:1.4.196:runtime
[INFO] \- org.springframework.boot:spring-boot-starter-test:jar:1.5.10.RELEASE:test
[INFO] +- org.springframework.boot:spring-boot-test:jar:1.5.10.RELEASE:test
[INFO] +- org.springframework.boot:spring-boot-test-autoconfigure:jar:1.5.10.RELEASE:test
[INFO] +- com.jayway.jsonpath:json-path:jar:2.2.0:test
[INFO] | \- net.minidev:json-smart:jar:2.2.1:test
[INFO] | \- net.minidev:accessors-smart:jar:1.1:test
[INFO] | \- org.ow2.asm:asm:jar:5.0.3:test
[INFO] +- junit:junit:jar:4.12:test
[INFO] +- org.assertj:assertj-core:jar:2.6.0:test
[INFO] +- org.mockito:mockito-core:jar:1.10.19:test
[INFO] | \- org.objenesis:objenesis:jar:2.1:test
[INFO] +- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] +- org.hamcrest:hamcrest-library:jar:1.3:test
[INFO] +- org.skyscreamer:jsonassert:jar:1.4.0:test
[INFO] | \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
[INFO] +- org.springframework:spring-core:jar:4.3.14.RELEASE:compile
[INFO] \- org.springframework:spring-test:jar:4.3.14.RELEASE:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
注意到了什么? 通过向项目中添加四个依赖项,添加了如此多的依赖项。 Spring Boot本身会收集所有相关的依赖项,因此在此方面不做任何事情。 最大的优势在于, 所有这些依赖项都保证相互兼容 。
3.构建文件的等效Gradle
尽管Maven是一个出色的构建系统,但如果您更喜欢Gradle,则以下是pom.xml
构建文件的Gradle等效项:
build.gradle
buildscript {ext {springBootVersion = '1.5.10.RELEASE'}repositories {mavenCentral()}dependencies {classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")}
}apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'group = 'com.javacodegeeks.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8repositories {mavenCentral()
}dependencies {compile('org.springframework.boot:spring-boot-starter-data-jpa')compile('org.springframework.boot:spring-boot-starter-web')runtime('com.h2database:h2')testCompile('org.springframework.boot:spring-boot-starter-test')
}
我们展示了完全相同的依赖项和版本,以提供完全相同的依赖项。
4.项目结构
在继续进行并开始处理该项目的代码之前,让我们在此介绍一下将所有代码添加到项目后将拥有的projet结构:
我们将项目分为多个包,以便遵循关注点分离的原则,并且代码保持模块化。
5.定义实体
让我们看看如何做到这一点:
人.java
package com.javacodegeeks.example.model;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;@Entity
public class Person {@Id@GeneratedValueprivate Long id;private String name;private int age;//standard getters and setters@Overridepublic String toString() {return String.format("Person{id=%d, name='%s', age=%d}", id, name, age);}
}
为了简洁起见,我们省略了标准的getter和setter方法,但是由于Jackson在对象的序列化和反序列化过程中使用它们,因此必须将它们制成。
@Entity
注释将该POJO标记为对象,该对象将由Spring Data API管理,并且其字段将被视为表列(除非标记为transient )。
最后,我们为toString()
方法添加了一个自定义实现,以便在测试应用程序时可以打印相关数据。
6.定义JPA存储库以访问H2数据库
JPA为我们提供了一种非常简单的定义JPA存储库接口的方法。
在了解如何定义JPA信息库之前,我们需要记住,只有在利用与JPA相关的功能时,才使每个JPA接口与数据库表的单个实体进行交互。 如果我们看一下接口定义,我们将对此有深刻的理解:
PersonRepository.java
package com.javacodegeeks.example.repository;import com.javacodegeeks.example.model.Person;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;@Repository
public interface PersonRepository extends JpaRepository<Person, Long> { }
尽管上面的接口定义是空的,但我们仍然需要了解一些要点:
-
@Repository
批注将此接口标记为Spring Bean,该Bean在应用程序启动时初始化。 有了这个注释,Spring可以很好地管理异常数据库的交互抛出 - 我们使用
Person
作为参数来表示此JPA接口将管理Person
实体 - 最后,我们还传递了数据类型
Long
作为参数。 这表示Person
实体包含一个唯一的标识符,该标识符的类型为Long
。
7.制作一个RESTful控制器
一个RESTful控制器,我们将应用程序的数据公开给客户端。 我们将使用GET,POST,PUT和DELETE等几个HTTP动词来支持与它们相关的功能。
首先,让我们定义一个PersonController
类,该类标记为@RestController
。 @RestController
注释向Spring容器发出信号, @RestController
中引发的任何异常都可以传递给客户端本身。 与存储库bean相比,这是不同的行为。
PersonController.java
package com.javacodegeeks.example.controller;import com.javacodegeeks.example.model.Person;
import com.javacodegeeks.example.repository.PersonRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
public class PersonController {private final Logger LOG = LoggerFactory.getLogger(getClass().getName());private final PersonRepository personRepository;@Autowiredpublic PersonController(PersonRepository personRepository) {this.personRepository = personRepository;}...
}
让我们通过添加执行实际操作的特定API为此Controller提供一些功能。
7.1使用POST插入数据
我们将首先添加一个API,通过该API我们可以将数据添加到H2数据库。 由于此方法在其@RequestBody
接受一个Person
对象,因此Person对象的JSON必须传递到请求中的API。
PersonController.java
@RequestMapping(value = "", method = RequestMethod.POST)
public Person createPerson(@RequestBody Person person) {LOG.info("Saving Person: {}.", person);personRepository.save(person);LOG.info("Person saved: {}.", person);return person;
}
我们使用Personrepository
Bean访问一种方法,该方法在我们先前定义的用于处理Person
Entity的JpaRepository
接口中预定义。
在接下来的部分中,我们将在REST客户端Postman中尝试此API。
7.2构造一个GET API
现在我们有了将数据插入数据库的API,我们可以构造一个API以获取ID为ID的Person对象。 在这里, personId
作为PathVariable
传递:
PersonController.java
@RequestMapping(value = "/{personId}", method = RequestMethod.GET)
public Person getPerson(@PathVariable Long personId) {Person person = personRepository.findOne(personId);LOG.info("Got person from DB: {}.", person);return person;
}
为了获取数据库中存在的所有数据,我们将使用另一个GET API从数据库中获取所有数据:
PersonController.java
@RequestMapping(value = "/all", method = RequestMethod.GET)
public List<Person> getAllPerson() {List<Person> persons = personRepository.findAll();LOG.info("Getting all Data: {}.", persons);return persons;
}
它只是利用JPA Repository的findAll
方法来获取数据库中Person
所有数据并将其收集到一个List中。
7.3使用PUT更新数据
现在,我们将允许客户端更新数据库中的现有数据。 为此,我们再次使用save
方法。 当save
方法看到JSON对象中填充了id
字段时,它首先找到具有该ID的Object,然后更新提供的字段。
PersonController.java
@RequestMapping(value = "", method = RequestMethod.PUT)
public Person editPerson(@RequestBody Person person) {LOG.info("Updating Person: {}.", person);personRepository.save(person);LOG.info("Person updated: {}.", person);return person;
}
7.4使用DELETE删除数据
现在,我们将完成最后的操作,即当一个人的ID作为PathVariable
传递时删除数据:
PersonController.java
@RequestMapping(value = "/{personId}", method = RequestMethod.DELETE)
public void deletePerson(@PathVariable Long personId) {LOG.info("Deleting Person with ID {}.", personId);personRepository.delete(personId);LOG.info("Person deleted.");
}
8.包含请求拦截器
尽管并非严格要求这样做,但在此示例中我们还将包括一个请求拦截器。 通过请求拦截器,我们可以在请求对象到达控制器之前对其进行处理。 一旦Controller完成请求并返回响应,该响应对象将再次通过Request Interceptor。 让我们在这里定义请求拦截器:
JCGRequestInterceptor.java
package com.javacodegeeks.example.interceptor;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Component
public class JCGRequestInterceptor extends HandlerInterceptorAdapter {private final Logger LOG = LoggerFactory.getLogger(getClass().getName());@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {LOG.info("Incoming request.");return super.preHandle(request, response, handler);}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {LOG.info("Outgoing request.");super.postHandle(request, response, handler, modelAndView);}
}
9.使用Maven运行项目
在运行项目之前,我们还需要为项目定义一个主类。 这是Request Interceptor bean的主要类定义:
运行项目
package com.javacodegeeks.example;import com.javacodegeeks.example.interceptor.JCGRequestInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;@SpringBootApplication
public class BootApp {public static void main(String[] args) {SpringApplication.run(BootApp.class, args);}@Beanpublic JCGRequestInterceptor requestInterceptor() {return new JCGRequestInterceptor();}
}
现在已经完成,我们可以运行我们的项目了。 使用maven可以轻松运行应用程序,只需使用以下命令:
运行项目
mvn spring-boot:run
现在该项目正在运行,我们可以使用Postman工具访问API,并查看它们是否按预期工作。
10.在Postman中访问API
我们将从使用我们制作的POST
API将一些数据插入H2数据库开始:
现在,我们可以为该Person对象分配ID JPA,即1:
我们还可以尝试获取所有数据API,以查看是否返回了此对象:
让我们更新我们创建的对象的字段之一:
最后,我们尝试通过将URL中的ID作为PathVariable
传递来删除数据:
11.包含和运行单元测试
没有至少一个单元测试用例,任何Spring应用程序都是不完整的。 在此示例中,我们将包括一个已准备就绪的单个单元测试用例。 它去了:
ControllerTests.java
package com.javacodegeeks.example;import com.javacodegeeks.example.controller.PersonController;
import com.javacodegeeks.example.repository.PersonRepository;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;@RunWith(SpringJUnit4ClassRunner.class)
public class ControllerTest {private MockMvc mockMvc;@InjectMocksprivate PersonController controller;@Mockprivate PersonRepository repository;@Beforepublic void setUp() {MockitoAnnotations.initMocks(this);controller = new PersonController(repository);mockMvc = MockMvcBuilders.standaloneSetup(controller).build();}@Testpublic void getAllPerson_Pass() throws Exception {mockMvc.perform(get("/all")).andExpect(status().is2xxSuccessful());}
}
我们在上述示例测试用例中做了几件事。 让我们一次了解这些:
- 我在
@RunWith(SpringJUnit4ClassRunner.class)
标记了@RunWith(SpringJUnit4ClassRunner.class)
批注,该批注为测试用例提供了运行程序。 - 使用Mockito时,需要启用其注释。 这是通过
initMocks(...)
方法调用完成的。 - 我们使用了
mockMvc
对象的get
mockMvc
来检查/all
GET API返回的状态码,并将其与任何2XX
状态码进行比较。
当我们在IntelliJ中运行此测试用例时,我们看到以下输出:
看到一个带有绿色的测试用例是一种很棒的感觉,不是吗?
12.使用Spring Boot CLI
Spring Boot命令行界面是一种软件,我们可以使用它从命令行运行和测试Spring Boot应用程序。 Spring Boot CLI在内部使用Spring Boot启动器和自动配置组件来收集所需的依赖关系并运行应用程序。
要开始使用CLI,最快的方法是下载ZIP并更改为bin目录,然后检查命令,如下所示:
要从任何地方使用Spring CLI,请将此JAR添加到PATH。
现在,让我们快速展示一下Spring Boot CLI的强大功能。 CLI可用于执行基于单个Groovy的nad运行脚本,而无需提供任何依赖关系。 我们将制作一个文件并将其命名为“ HelloWorld.groovy”。 请在此处注意文件扩展名,因为有必要使该文件仅是groovy类型的。 在文件中,我们将放置简单的代码片段:
HelloWorld.groovy
@RestController
class HelloWorld {@RequestMapping("/")String hello() {"Hello World!"}
}
现在,移至创建此脚本的文件夹,然后运行以下命令:
HelloWorld.groovy
spring run HelloWorld.groovy
此命令将在默认端口8080上运行上述Grovvy脚本。让我们现在尝试访问此端口:
容易吗? 请注意,编写Groovy脚本时没有依赖关系,没有配置,也没有导入语句。 这是因为Spring Boot核心组件,Groovy编译器( groovyc
)和Groovy Grape(Groovy的JAR依赖管理器)承担了这一责任。
13.结论
在本课程中,我们研究了使用Spring Boot构建生产级API的便捷性。 我们设法制作了一些功能齐全的API,这些API可以与数据库通信并可以运行单元测试用例。
我们在生产级RESTful客户端Postman中试用了API,并看到我们的API可以按预期响应调用。 我们在本课中使用的H2数据库很容易被MySQL,MongoDB或任何其他数据库之类的真实数据库替换。
14.下载源代码
在这个例子中,我们研究了如何开始一个基本的Spring Boot项目。
您可以在此处下载此示例的完整源代码: JCG-SpringBoot-Example
翻译自: https://www.javacodegeeks.com/2018/04/spring-boot-tutorial.html