ZetCode-Spring-Boot-教程-一-
ZetCode Spring Boot 教程(一)
原文:ZetCode
Spring Boot Flash 属性
Spring Boot Flash 属性教程展示了如何在 Spring Boot 应用中创建 Flash 消息。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。
Flash 消息是用于用户通知或存储表单输入的临时数据。 它们存储在一个会话中,并且一旦检索就消失。
使用RedirectAttributes
的addFlashAttribute()
在 Spring 中将 Flash 消息创建为 Flash 属性。 它们与RedirectView
结合使用。
Spring Boot Flash 属性示例
在以下应用中,我们创建用于通知和记住表单输入值的 Flash 属性。 我们有一个带有两个输入的表格。 如果输入值不符合验证标准,则应用将重定向到表单页面并显示错误消息; 这些消息作为 Flash 属性发送。
此外,还可以记住表单的正确值。
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ └───controller
│ │ MyController.java
│ │
│ └───resources
│ └───templates
│ index.html
│ showMessage.html
└───test
└───java
这是 Spring 应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springflashmessage</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<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.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.13.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven pom.xml
文件。 我们使用spring-boot-starter-thymeleaf
与 Thymeleaf 进行模板化,并使用hibernate-validator
进行表单数据验证。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;
import org.thymeleaf.util.StringUtils;
import javax.validation.ConstraintViolationException;
import javax.validation.constraints.Size;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@Controller
@Validated
public class MyController {
@RequestMapping("/")
public String index(Model model) {
return "index";
}
@RequestMapping("/message")
public ModelAndView message(@RequestParam @Size(min = 2, max = 255) String name,
@RequestParam @Size(min = 2, max = 255) String occupation) {
var msg = String.format("%s is a %s", name, occupation);
Map<String, Object> params = new HashMap<>();
params.put("message", msg);
return new ModelAndView("showMessage", params);
}
@ExceptionHandler(ConstraintViolationException.class)
public RedirectView handleError(ConstraintViolationException ex,
WebRequest request,
RedirectAttributes atts) {
var name = request.getParameter("name");
var occupation = request.getParameter("occupation");
var errorMessages = new ArrayList<String>();
var violations = ex.getConstraintViolations();
violations.forEach(violation -> {
var error = String.format("%s: %s", violation.getPropertyPath(),
violation.getMessage());
errorMessages.add(error);
});
if (!StringUtils.isEmptyOrWhitespace(name)) {
atts.addFlashAttribute("name", name);
}
if (!StringUtils.isEmptyOrWhitespace(occupation)) {
atts.addFlashAttribute("occupation", occupation);
}
atts.addFlashAttribute("messages", errorMessages);
var redirectView = new RedirectView("/");
return redirectView;
}
}
这是MyController
。 它响应来自客户端的请求。 它找出当前日期和时间,并将处理过程解析为showMessage.ftl
模板,并将其传递给数据。
@Controller
@Validated
public class MyController {
@Validated
注解验证带注解的请求参数。 在我们的例子中,我们使用两个@Size
注解。
@RequestMapping("/")
public String index(Model model) {
return "index";
}
根页面返回索引视图,该视图将表单发送给客户端。
@RequestMapping("/message")
public ModelAndView message(@RequestParam @Size(min = 2, max = 255) String name,
@RequestParam @Size(min = 2, max = 255) String occupation) {
var msg = String.format("%s is a %s", name, occupation);
Map<String, Object> params = new HashMap<>();
params.put("message", msg);
return new ModelAndView("showMessage", params);
}
此操作响应表单提交。 两个输入参数,名称和职业用@Size
注解。 如果一切正常,将根据参数构建一条消息,并使用showMessage
视图将其发送到客户端。
@ExceptionHandler(ConstraintViolationException.class)
public RedirectView handleError(ConstraintViolationException ex,
WebRequest request,
RedirectAttributes atts) {
如果输入参数未能通过验证,则会抛出ConstraintViolationException
。 我们在提供的异常处理器中对异常做出反应。
var name = request.getParameter("name");
var occupation = request.getParameter("occupation");
我们得到了请求参数。 它们用于保留正确的表单输入值。
var errorMessages = new ArrayList<String>();
var violations = ex.getConstraintViolations();
violations.forEach(violation -> {
var error = String.format("%s: %s", violation.getPropertyPath(),
violation.getMessage());
errorMessages.add(error);
});
我们得到约束违例并建立错误消息列表。 错误消息将显示在表单上方的索引表单页面中。
if (!StringUtils.isEmptyOrWhitespace(name)) {
atts.addFlashAttribute("name", name);
}
if (!StringUtils.isEmptyOrWhitespace(occupation)) {
atts.addFlashAttribute("occupation", occupation);
}
如果填充的输入参数不为空并且不仅包含空格,则将它们与addFlashAttribute()
一起存储为 Flash 属性。
atts.addFlashAttribute("messages", errorMessages);
错误消息存储为 Flash 属性。
var redirectView = new RedirectView("/");
return redirectView;
我们使用RedirectView
重定向到表单页面。
templates/index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Home page</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.1/semantic.min.css"
rel="stylesheet">
</head>
<body>
<section class="ui container">
<ul th:each="message : ${messages}">
<li th:text="${message}" class="ui error message" />
</ul>
<form class="ui form" action="message" method="post">
<div class="field">
<label>Name:</label>
<input type="text" name="name" th:value="${name}">
</div>
<div class="field">
<label>Occupation:</label>
<input type="text" name="occupation" th:value="${occupation}">
</div>
<button class="ui button" type="submit">Send</button>
</form>
</section>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.1/semantic.min.js"></script>
</body>
</html>
这是主页模板。 它发送带有两个输入的表单:名称和职业。 样式是使用语义 UI 库完成的。
<ul th:each="message : ${messages}">
<li th:text="${message}" class="ui error message" />
</ul>
如果有任何错误消息,将显示它们。
templates/showMessage.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Message</title>
</head>
<body>
<p th:text="${message}"/>
</body>
</html>
成功处理表单后,showMessage
模板会显示一条消息。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
是设置 Spring Boot 应用的入口。
在本教程中,我们展示了如何在 Spring 应用中使用 flash 属性。 您可能也对相关教程感兴趣: Spring Boot @RestController
教程, Spring Boot @ExceptionHandler
教程, Spring Boot 上传文件, Spring Boot @RequestParam
教程, Java 教程。
Spring Boot CrudRepository
教程
SpringBoot CrudRepository
教程展示了如何使用CrudRepository
在 Spring Boot 应用中管理数据。
Spring 是流行的 Java 应用框架。 Spring Boot 致力于以最小的努力创建独立的,基于生产级别的基于 Spring 的应用。
Spring Data
Spring Data 是用于数据访问的基于 Spring 的编程模型。 它减少了使用数据库和数据存储所需的代码量。 它由几个模块组成。 Spring Data JPA 简化了使用 JPA 技术的 Spring 应用的开发。
使用 Spring Data,我们为应用中的每个域实体定义了一个存储库接口。 存储库包含用于执行 CRUD 操作,对数据进行排序和分页的方法。 @Repository
是标记注解,指示基础接口是存储库。 通过扩展特定的存储库接口(例如CrudRepository
,PagingAndSortingRepository
或JpaRepository
)来创建存储库。
Spring Data 已与 Spring MVC 控制器进行了高级集成,并提供了从存储库方法名称派生的动态查询。
CrudRepository
CrudRepository
实现基本的 CRUD 操作,包括count
,delte
,deleteById
,save
,saveAll
,findById
和findAll
。
Spring Boot CrudRepository
示例
以下 Spring Boot 应用使用CrudRepository
管理User
实体。 数据保存在 H2 数据库中。 我们使用一个 RESTful 控制器。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ ├───controller
│ │ │ MyController.java
│ │ ├───model
│ │ │ User.java
│ │ ├───repository
│ │ │ UserRepository.java
│ │ └───service
│ │ UserService.java
│ └───resources
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>crudrepositoryex</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 spring-boot-starter-web
是使用 Spring MVC 构建 Web(包括 RESTful)应用的入门程序。 spring-boot-starter-data-jpa
是将 Spring Data JPA 与 Hibernate 结合使用的入门工具。
com/zetcode/model/User.java
package com.zetcode.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.Objects;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String email;
public User() {}
public User(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) &&
Objects.equals(firstName, user.firstName) &&
Objects.equals(lastName, user.lastName) &&
Objects.equals(email, user.email);
}
@Override
public int hashCode() {
return Objects.hash(id, firstName, lastName, email);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("User{");
sb.append("id=").append(id);
sb.append(", firstName='").append(firstName).append('\'');
sb.append(", lastName='").append(lastName).append('\'');
sb.append(", email='").append(email).append('\'');
sb.append('}');
return sb.toString();
}
}
这是User
实体。
com/zetcode/service/UserService.java
package com.zetcode.service;
import com.zetcode.model.User;
import com.zetcode.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public List<User> findAll() {
var it = userRepository.findAll();
var users = new ArrayList<User>();
it.forEach(e -> users.add(e));
return users;
}
public Long count() {
return userRepository.count();
}
public void deleteById(Long userId) {
userRepository.deleteById(userId);
}
}
这是服务类。 该类提供了三种方法来查找所有用户,对用户进行计数以及按 ID 删除用户。
@Service
public class UserService {
在 Spring 中,服务类用@Service
注解修饰。
@Autowired
private UserRepository userRepository;
我们注入UserRepository
。
public List<User> findAll() {
var it = userRepository.findAll();
var users = new ArrayList<User>();
it.forEach(e -> users.add(e));
return users;
}
findAll()
方法调用userRepository
的findAll()
方法并检索所有用户。
com/zetcode/repository/UserRepository.java
package com.zetcode.repository;
import com.zetcode.model.User;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends CrudRepository<User, Long> {
}
UserRepository
从CrudRepository
延伸。 它提供了实体的类型及其主键。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import com.zetcode.model.User;
import com.zetcode.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class MyController {
@Autowired
private UserService userService;
@GetMapping("/users")
public List<User> allUsers() {
return userService.findAll();
}
@GetMapping("/users/count")
public Long count() {
return userService.count();
}
@DeleteMapping("/users/{id}")
public void delete(@PathVariable String id) {
Long userId = Long.parseLong(id);
userService.deleteById(userId);
}
}
控制器类提供了三个请求的映射。 我们可以获取所有用户,计算用户数,并通过其 ID 删除用户。 数据以 JSON 格式返回。
@GetMapping("/users")
public List<User> allUsers() {
return userService.findAll();
}
为了获得所有用户,我们使用@GetMapping
注解。
@DeleteMapping("/users/{id}")
public void delete(@PathVariable String id) {
Long userId = Long.parseLong(id);
userService.deleteById(userId);
}
我们使用@DeleteMapping
删除特定用户。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。
com/zetcode/MyRunner.java
package com.zetcode;
import com.zetcode.model.User;
import com.zetcode.repository.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import javax.transaction.Transactional;
@Component
public class MyRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);
@Autowired
private UserRepository userRepository;
@Override
@Transactional
public void run(String... args) throws Exception {
logger.info("initializing users");
var u1 = new User("Paul", "Smith", "paul.smith@gmail.com");
userRepository.save(u1);
var u2 = new User("Robert", "Black", "rb34@gmail.com");
userRepository.save(u2);
var u3 = new User("John", "Doe", "jdoe@gmail.com");
userRepository.save(u3);
}
}
在MyRunner
中,我们为应用设置了数据。
var u1 = new User("Paul", "Smith", "paul.smith@gmail.com");
userRepository.save(u1);
我们创建一个新用户,并使用存储库的save()
方法将其保存。
$ curl localhost:8080/users
[{"id":1,"firstName":"Paul","lastName":"Smith","email":"paul.smith@gmail.com"},
{"id":2,"firstName":"Robert","lastName":"Black","email":"rb34@gmail.com"},
{"id":3,"firstName":"John","lastName":"Doe","email":"jdoe@gmail.com"}]
我们使用curl
工具测试该应用。
在本教程中,我们使用CrudRepository
管理了应用数据。 您可能也对相关教程感兴趣: Spring Boot 首次 Web 应用, Spring Boot 数据 JPA @Query
教程, Spring Boot 数据 JPA @NamedQuery
教程, 独立的 Spring 应用和 Java 教程。
Spring Boot JpaRepository
教程
SpringBoot JpaRepository
教程展示了如何使用JpaRepository
在 Spring Boot 应用中管理数据。
Spring 是流行的 Java 应用框架。 Spring Boot 致力于以最小的努力创建独立的,基于生产级别的基于 Spring 的应用。
Spring Data
Spring Data 是用于数据访问的基于 Spring 的编程模型。 它减少了使用数据库和数据存储所需的代码量。 它由几个模块组成。 Spring Data JPA 简化了使用 JPA 技术的 Spring 应用的开发。
使用 Spring Data,我们为应用中的每个域实体定义了一个存储库接口。 存储库包含用于执行 CRUD 操作,对数据进行排序和分页的方法。 @Repository
是标记注解,指示基础接口是存储库。 通过扩展特定的存储库接口(例如CrudRepository
,PagingAndSortingRepository
或JpaRepository
)来创建存储库。
Spring Data 已与 Spring MVC 控制器进行了高级集成,并提供了从存储库方法名称派生的动态查询。
JpaRepository
JpaRepository
是Repository
的 JPA 特定扩展。 它包含CrudRepository
和PagingAndSortingRepository
的完整 API。 因此,它包含用于基本 CRUD 操作的 API,以及用于分页和排序的 API。
Spring Boot JpaRepository
示例
以下 Spring Boot 应用使用JpaRepository
管理City
实体。 数据保存在 H2 数据库中。 该应用是一个控制台程序。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ ├───model
│ │ │ City.java
│ │ └───repository
│ │ CityRepository.java
│ └───resources
│ application.properties
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootjparepository</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 spring-boot-starter-data-jpa
是将 Spring Data JPA 与 Hibernate 结合使用的入门工具。
resources/application.properties
spring.main.banner-mode=off
logging.pattern.console=%clr(%d{yy-MM-dd E HH:mm:ss.SSS}){blue} %clr(%-5p) %clr(%logger{0}){blue} %clr(%m){faint}%n
application.properties
是主要的 Spring Boot 配置文件。 使用spring.main.banner-mode
属性,我们可以关闭 Spring 横幅。 logging.pattern.console
定义控制台的日志模式。
com/zetcode/model/City.java
package com.zetcode.model;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "cities")
public class City {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int population;
public City() {
}
public City(String name, int population) {
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.id);
hash = 79 * hash + Objects.hashCode(this.name);
hash = 79 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append("City{id=").append(id).append(", name=")
.append(name).append(", population=")
.append(population).append("}");
return builder.toString();
}
}
这是City
实体。 它包含以下属性:id
,name
和population
。
com/zetcode/repository/CityRepository.java
package com.zetcode.repository;
import com.zetcode.model.City;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CityRepository extends JpaRepository<City, Long> {
}
CityRepository
从JpaRepository
延伸。 它提供了实体的类型及其主键。
注意:在Java企业应用中,定义与存储库一起使用的服务层是一个好习惯。 为简单起见,我们跳过服务层。
com/zetcode/MyRunner.java
package com.zetcode;
import com.zetcode.model.City;
import com.zetcode.repository.CityRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);
@Autowired
private CityRepository cityRepository;
@Override
public void run(String... args) throws Exception {
cityRepository.save(new City("Bratislava", 432000));
cityRepository.save(new City("Budapest", 1759000));
cityRepository.save(new City("Prague", 1280000));
cityRepository.save(new City("Warsaw", 1748000));
cityRepository.save(new City("Los Angeles", 3971000));
cityRepository.save(new City("New York", 8550000));
cityRepository.save(new City("Edinburgh", 464000));
logger.info("# of cities: {}", cityRepository.count());
logger.info("All cities unsorted:");
var cities = cityRepository.findAll();
logger.info("{}", cities);
logger.info("------------------------");
logger.info("All cities sorted by name in descending order");
var sortedCities = cityRepository.findAll(new Sort(Sort.Direction.DESC, "name"));
logger.info("{}", sortedCities);
logger.info("------------------------");
logger.info("Deleting all cities");
cityRepository.deleteAllInBatch();
logger.info("# of cities: {}", cityRepository.count());
}
}
在MyRunner
中,我们使用JpaRepository
的各种方法。
@Autowired
private CityRepository cityRepository;
我们将CityRepository
注入cityRepository
字段。
cityRepository.save(new City("Bratislava", 432000));
使用save()
插入一个新城市。
logger.info("# of cities: {}", cityRepository.count());
我们用count()
计算城市数。
logger.info("All cities unsorted:");
var cities = cityRepository.findAll();
logger.info("{}", cities);
使用findAll()
,我们可以到达所有城市。
logger.info("All cities sorted by name in descending order");
var sortedCities = cityRepository.findAll(new Sort(Sort.Direction.DESC, "name"));
logger.info("{}", sortedCities);
通过将Sort
对象传递给findAll()
方法,我们得到了所有城市的降序排列。
logger.info("Deleting all cities");
cityRepository.deleteAllInBatch();
我们使用deleteAllInBatch()
批量删除所有城市。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。
...
19-06-25 Tue 12:47:14.593 INFO MyRunner # of cities: 7
19-06-25 Tue 12:47:14.593 INFO MyRunner All cities unsorted:
19-06-25 Tue 12:47:14.652 INFO MyRunner [City{id=1, name=Bratislava, population=432000}, City{id=2, name=Budapest, population=1759000}, City{id=3, name=Prague, population=1280000}, City{id=4, name=Warsaw, population=1748000}, City{id=5, name=Los Angeles, population=3971000}, City{id=6, name=New York, population=8550000}, City{id=7, name=Edinburgh, population=464000}]
19-06-25 Tue 12:47:14.652 INFO MyRunner ------------------------
19-06-25 Tue 12:47:14.652 INFO MyRunner All cities sorted by name in descending order
19-06-25 Tue 12:47:14.667 INFO MyRunner [City{id=4, name=Warsaw, population=1748000}, City{id=3, name=Prague, population=1280000}, City{id=6, name=New York, population=8550000}, City{id=5, name=Los Angeles, population=3971000}, City{id=7, name=Edinburgh, population=464000}, City{id=2, name=Budapest, population=1759000}, City{id=1, name=Bratislava, population=432000}]
19-06-25 Tue 12:47:14.668 INFO MyRunner ------------------------
19-06-25 Tue 12:47:14.668 INFO MyRunner Deleting all cities
19-06-25 Tue 12:47:14.681 INFO MyRunner # of cities: 0
...
这是一个示例输出。
在本教程中,我们使用JpaRepository
管理了应用数据。 您可能也对相关教程感兴趣: Spring Boot CrudRepository
教程, Spring Boot Data JPA @Query
教程, Spring Boot Data JPA @NamedQuery
教程,Java 教程。
Spring Boot findById
教程
Spring Boot findById
教程展示了如何使用CrudRepository
的findById
方法通过其 ID 检索实体。
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架的演进,可帮助您轻松创建独立的,生产级的基于 Spring 的应用。
CrudRepository
CrudRepository
接口在存储库中为特定类型提供通用 CRUD 操作。 其findById()
方法通过其 ID 检索实体。 返回值为Optional<T>
。
Optional<T>
是一个容器对象,可能包含也可能不包含非空值。 如果存在值,则isPresent()
返回true
,get()
返回该值。 如果存在该值,则ifPresent()
会调用指定的方法; 否则什么都不做。
Spring Boot findById
示例
以下应用设置了City
对象的存储库。 在控制台运行器中,我们通过其 ID 检索城市对象。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ ├───model
│ │ │ City.java
│ │ ├───repository
│ │ │ CityRepository.java
│ │ └───service
│ │ CityService.java
│ │ ICityService.java
│ └───resources
│ application.properties
│ data-h2.sql
│ schema-h2.sql
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>sprinbootfindbyid</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Maven POM 文件包含 Spring Data JPA 和 H2 数据库的依赖项。
resources/application.properties
spring.main.banner-mode=off
spring.datasource.platform=h2
spring.jpa.hibernate.ddl-auto=none
application.properties
是 Spring Boot 的主要配置文件。 Spring Boot 横幅使用spring.main.banner-mode
属性关闭。
spring.datasource.platform
设置数据库的供应商名称。 在初始化脚本中使用它。 spring.jpa.hibernate.ddl-auto
禁止从实体自动创建模式。
resources/schema-h2.sql
CREATE TABLE cities(id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255), population INT);
启动应用时,将执行schema-h2.sql
脚本。 它创建一个新的数据库表。
resources/data-h2.sql
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Suzhou', 4327066);
INSERT INTO cities(name, population) VALUES('Zhengzhou', 4122087);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
该表中填充了data-h2.sql
文件中的数据。
com/zetcode/model/City.java
package com.zetcode.model;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "cities")
public class City {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int population;
public City() {
}
public City(String name, int population) {
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.id);
hash = 79 * hash + Objects.hashCode(this.name);
hash = 79 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append("City{id=").append(id).append(", name=")
.append(name).append(", population=")
.append(population).append("}");
return builder.toString();
}
}
这是City
实体。
com/zetcode/repository/CityRepository.java
package com.zetcode.repository;
import com.zetcode.model.City;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CityRepository extends CrudRepository<City, Long> {
}
通过从 Spring CrudRepository
扩展,我们为实现数据库提供了一些基本方法。
com/zetcode/service/ICityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import java.util.Optional;
public interface ICityService {
Optional<City> findById(Long id);
}
ICityService
提供了一种通过城市 ID 来获取城市的契约方法。
com/zetcode/service/CityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import com.zetcode.repository.CityRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class CityService implements ICityService {
@Autowired
private CityRepository cityRepository;
@Override
public Optional<City> findById(Long id) {
return cityRepository.findById(id);
}
}
CityService
包含findById()
方法的实现。 我们使用存储库从数据库检索数据。
@Autowired
private CityRepository repository;
注入CityRepository
。
@Override
public Optional<City> findById(Long id) {
return cityRepository.findById(id);
}
findById()
返回Optional<City>
。
com/zetcode/MyRunner.java
package com.zetcode;
import com.zetcode.service.ICityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
@Autowired
private ICityService cityService;
@Override
public void run(String... args) throws Exception {
var id1 = 2L;
cityService.findById(id1).ifPresent(System.out::println);
var id2 = 24L;
var val = cityService.findById(id2);
if (val.isPresent()) {
System.out.println(val.get());
} else {
System.out.printf("No city found with id %d%n", id2);
}
}
}
在MyRunner
中,我们寻找两个城市实体。
var id1 = 2L;
cityService.findById(id1).ifPresent(System.out::println);
我们通过其 ID 查找城市,如果存在,则将其打印到控制台。 否则,不打印任何内容。
var id2 = 24L;
var val = cityService.findById(id2);
if (val.isPresent()) {
System.out.println(val.get());
} else {
System.out.printf("No city found with id %d%n", id2);
}
在第二种情况下,我们使用isPresent()
检查值的存在。 如果存在值,我们将其打印出来。 如果没有,我们会打印一条消息,提示找不到该城市。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
$ mvn spring-boot:run -q
...
City{id=2, name=Budapest, population=1759000}
No city found with id 24
...
我们运行该应用。
在本教程中,我们展示了如何使用CrudRepository
的findById()
方法查找特定实体。 您可能也对相关教程感兴趣:
- Spring Boot Data JPA
@NamedQuery
教程 - Spring Boot REST Data JPA 教程
- Spring Boot Data JPA 排序教程
- Spring Boot
CrudRepository
教程
Spring Boot Data JPA @NamedQuery
教程
Spring Boot Data JPA @NamedQuery
教程展示了如何使用 JPA @NamedQuery
创建自定义查询。
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架的演进,可帮助您轻松创建独立的,生产级的基于 Spring 的应用。
Spring Data JPA
Spring Data JPA 有助于实现基于 JPA 的存储库。 它增强了对基于 JPA 的数据访问层的支持。 它使构建使用数据访问技术的 Spring 支持的应用变得更加容易。 Spring Data JPA 是较大的 Spring Data 系列的一部分。
JPA @NamedQuery
@NamedQuery
注解是我们创建的预定义查询,并与容器管理的实体相关联。 @Query
注解是类似的注解,它直接在存储库方法上声明查找程序查询。 在域类上使用@NamedQuery
时,在Repository
接口上使用 Spring Data JPA @Query
注解。 这样可以将域类从持久性特定的信息中解放出来,这是一件好事。
Spring Boot Data JPA @NamedQuery
示例
以下应用是一个简单的 Spring Boot Web 应用,它使用 JPA @NamedQuery
创建一个自定义查询。 该示例的数据存储在基于内存的 H2 数据库中。 数据库在应用启动时初始化。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ ├───controller
│ │ │ MyController.java
│ │ ├───model
│ │ │ City.java
│ │ ├───repository
│ │ │ CityRepository.java
│ │ └───service
│ │ CityService.java
│ │ ICityService.java
│ └───resources
│ │ application.properties
│ │ data-h2.sql
│ │ schema-h2.sql
│ ├───static
│ │ index.html
│ └───templates
│ showCities.ftl
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootdatajpanamedquery</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Maven POM 文件包含 H2 数据库,Freemarker 和 Spring Boot Data JPA 的依赖项。
resources/application.properties
spring.main.banner-mode=off
spring.datasource.platform=h2
logging.level.org.hibernate.SQL=DEBUG
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
在application.properties
文件中,我们有各种配置设置。 使用spring.main.banner-mode
属性,我们可以关闭 Spring 横幅。
spring.datasource.platform
设置数据库的供应商名称。 在初始化脚本中使用它。 spring.jpa.show-sql
允许记录 SQL 语句。 最后,spring.jpa.hibernate.ddl-auto
禁止从实体自动创建模式。
com/zetcode/model/City.java
package com.zetcode.model;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
@Entity
@Table(name = "cities")
@NamedQuery(name = "City.findAllOrderedByNameDescending",
query = "SELECT c FROM City c ORDER BY c.name DESC")
public class City {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int population;
public City() {
}
public City(String name, int population) {
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.id);
hash = 79 * hash + Objects.hashCode(this.name);
hash = 79 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append("City{id=").append(id).append(", name=")
.append(name).append(", population=")
.append(population).append("}");
return builder.toString();
}
}
这是City
实体。 它包含一个命名查询。
@Entity
@Table(name = "cities")
@NamedQuery(name = "City.findAllOrderedDescending",
query = "SELECT c FROM City c ORDER BY c.name DESC")
public class City {
@Entity
注解指定该类是一个实体,并映射到数据库表。 @Table
注解指定要用于映射的数据库表的名称。 @NamedQuery
定义了一个命名查询,该查询返回按名称降序排列的所有城市。
resources/schema-h2.sql
CREATE TABLE cities(id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255), population INT);
启动应用时,将执行schema-h2.sql
脚本。 它创建一个新的数据库表。
resources/data-h2.sql
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Suzhou', 4327066);
INSERT INTO cities(name, population) VALUES('Zhengzhou', 4122087);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
之后,执行data-h2.sql
文件。 它用数据填充表。
com/zetcode/CityRepository.java
package com.zetcode.repository;
import com.zetcode.model.City;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CityRepository extends CrudRepository<City, Long> {
List<City> findAllOrderedByNameDescending();
}
我们将findAllOrderedByNameDescending()
的声明添加到存储库接口中。
com/zetcode/service/ICityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import java.util.List;
public interface ICityService {
List<City> findAllOrderedByNameDescending();
}
ICityService
包含一种契约方法,用于按名称降序排列所有城市。
com/zetcode/service/CityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import com.zetcode.repository.CityRepository;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CityService implements ICityService {
@Autowired
private CityRepository repository;
@Override
public List<City> findAllOrderedByNameDescending() {
var cities = (List<City>) repository.findAllOrderedByNameDescending();
return cities;
}
}
CityService
包含findAllOrderedByNameDescending()
方法的实现。 我们使用存储库从数据库检索数据。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import com.zetcode.model.City;
import com.zetcode.service.ICityService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class MyController {
@Autowired
private ICityService cityService;
@GetMapping("/showCities")
public String findAllOrderedByNameDescending(Model model) {
var cities = (List<City>) cityService.findAllOrderedByNameDescending();
model.addAttribute("cities", cities);
return "showCities";
}
}
MyController
类用@Controller
注解。
@Autowired
private ICityService cityService;
我们在countryService
字段中插入ICityService
。
@GetMapping("/showCities")
public String findAllOrderedByNameDescending(Model model) {
var cities = (List<City>) cityService.findAllOrderedByNameDescending();
System.out.println(cities);
model.addAttribute("cities", cities);
return "showCities";
}
我们将带有showCities
路径的请求映射到控制器的findAllOrderedByNameDescending()
方法。 该模型将获得按名称降序排列的城市列表,并将处理发送到showCities.ftl
Freemarker 模板文件。
resources/templates/showCities.ftl
<!DOCTYPE html>
<html>
<head>
<title>Cities</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h2>List of cities ordered by name in descending order</h2>
<table>
<tr>
<th>Id</th>
<th>Name</th>
<th>Population</th>
</tr>
<#list cities as city>
<tr>
<td>${city.id}</td>
<td>${city.name}</td>
<td>${city.population}</td>
</tr>
</#list>
</table>
</body>
</html>
在showCities.ftl
模板文件中,我们在 HTML 表中显示数据。
resources/static/index.html
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<a href="showCities">Show ordered cities by name in descending order</a>
</body>
</html>
在index.html
中有一个链接,显示规定的城市。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
$ mvn spring-boot:run
应用运行后,我们可以导航到localhost:8080
。
在本教程中,我们展示了如何在 Spring Boot 应用中使用 JPA @NamedQuery
注解创建自定义 JPQL 查询。 您可能也对相关教程感兴趣: Spring Boot Data JPA @Query
教程, Spring Boot CrudRepository
教程, Spring Boot REST Data JPA 教程或列出所有 Spring Boot 教程。
Spring Boot Data JPA @Query
教程
Spring Boot Data JPA @Query
教程展示了如何使用 Data JPA @Query
创建自定义查询。
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架的演进,可帮助您轻松创建独立的,生产级的基于 Spring 的应用。
Spring Data JPA
Spring Data JPA 有助于实现基于 JPA 的存储库。 它增强了对基于 JPA 的数据访问层的支持。 它使构建使用数据访问技术的 Spring 支持的应用变得更加容易。 Spring Data JPA 是较大的 Spring Data 系列的一部分。
Spring Data JPA @Query
@Query
注解直接在存储库方法上声明查找程序查询。 虽然在域类上使用了类似的@NamedQuery
,但在Repository
接口上使用了 Spring Data JPA @Query
注解。 这样可以将域类从持久性特定的信息中解放出来,这是一件好事。
Spring Boot Data JPA @Query
示例
以下应用是一个简单的 Spring Boot Web 应用,它使用 Data JPA @Query 创建一个自定义查询。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ ├───controller
│ │ │ MyController.java
│ │ ├───model
│ │ │ City.java
│ │ ├───repository
│ │ │ CityRepository.java
│ │ └───service
│ │ CityService.java
│ │ ICityService.java
│ └───resources
│ │ application.properties
│ │ data-h2.sql
│ │ schema-h2.sql
│ ├───static
│ │ index.html
│ └───templates
│ showCities.ftl
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootcustomdatajpaquery</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Maven POM 文件包含 H2 数据库,Freemarker 模板引擎和 Spring Boot Data JPA 的依赖项。
resources/application.properties
server.servlet.contextPath=/myapp
spring.main.banner-mode=off
spring.datasource.platform=h2
logging.level.org.hibernate.SQL=DEBUG
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
在application.properties
文件中,我们编写了 Spring Boot 应用的各种配置设置。 server.servlet.contextPath
设置上下文路径(应用名称)。 完成这些设置后,我们可以通过localhost:8080/myapp/
访问该应用。 使用spring.main.banner-mode
属性,我们可以关闭 Spring 横幅。
spring.datasource.platform
设置数据库的供应商名称。 在初始化脚本中使用它。 spring.jpa.show-sql
允许记录 SQL 语句。 最后,spring.jpa.hibernate.ddl-auto
禁止从实体自动创建模式。
com/zetcode/model/City.java
package com.zetcode.model;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "cities")
public class City {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int population;
public City() {
}
public City(String name, int population) {
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.id);
hash = 79 * hash + Objects.hashCode(this.name);
hash = 79 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append("City{id=").append(id).append(", name=")
.append(name).append(", population=")
.append(population).append("}");
return builder.toString();
}
}
这是City
实体。 每个实体必须至少定义两个注解:@Entity
和@Id
。
@Entity
@Table(name = "cities")
public class City {
@Entity
注解指定该类是一个实体,并映射到数据库表。 @Table
注解指定要用于映射的数据库表的名称。
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Id
注解指定实体的主键,@GeneratedValue
提供规范主键值的生成策略。
resources/schema-h2.sql
CREATE TABLE cities(id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255), population INT);
启动应用时,将执行schema-h2.sql
脚本。 它创建一个新的数据库表。
resources/data-h2.sql
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Suzhou', 4327066);
INSERT INTO cities(name, population) VALUES('Zhengzhou', 4122087);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
之后,执行data-h2.sql
文件。 它用数据填充表。
com/zetcode/repository/CityRepository.java
package com.zetcode.repository;
import com.zetcode.model.City;
import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CityRepository extends CrudRepository<City, Long> {
@Query("select c from City c where c.name like %?1")
List<City> findByNameEndsWith(String chars);
}
通过从 Spring CrudRepository
扩展,我们将为数据存储库实现一些方法。 另外,我们创建一个自定义的findByNameEndsWith()
方法。
@Query("select c from City c where c.name like %?1")
List<City> findByNameEndsWith(String chars);
@Query
注解包含自定义 JPQL 查询。 它返回名称以提供的字符结尾的城市。
com/zetcode/service/ICityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import java.util.List;
public interface ICityService {
List<City> findByNameEndsWith(String name);
}
ICityService
提供了一种获取所有以特定字符结尾的城市的契约方法。
com/zetcode/service/CityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import com.zetcode.repository.CityRepository;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CityService implements ICityService {
@Autowired
private CityRepository repository;
@Override
public List<City> findByNameEndsWith(String name) {
var cities = (List<City>) repository.findByNameEndsWith(name);
return cities;
}
}
CityService
包含findByNameEndsWith()
方法的实现。 我们使用存储库从数据库检索数据。
@Autowired
private CityRepository repository;
注入CityRepository
。
var cities = (List<City>) repository.findByNameEndsWith(name);
存储库的findByNameEndsWith()
方法返回以某些字符结尾的城市列表。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import com.zetcode.model.City;
import com.zetcode.service.ICityService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class MyController {
@Autowired
ICityService cityService;
@GetMapping("/showCitiesEnding")
public String findCitiesNameEndsWith(Model model, @RequestParam String name) {
var cities = (List<City>) cityService.findByNameEndsWith(name);
model.addAttribute("cities", cities);
return "showCities";
}
}
MyController
类用@Controller
注解。
@Autowired
private ICityService cityService;
我们在cityService
字段中插入ICityService
。
@GetMapping("/showCitiesEnding")
public String findCitiesNameEndsWith(Model model, @RequestParam String name) {
var cities = (List<City>) cityService.findByNameEndsWith(name);
model.addAttribute("cities", cities);
return "showCities";
}
我们将带有showCitiesEnding
路径的请求映射到控制器的findCitiesNameEndsWith()
方法。 该模型将获取匹配城市的列表,并将处理过程发送到showCities.ftl
Freemarker 模板文件。
resources/templates/showCities.ftl
<!DOCTYPE html>
<html>
<head>
<title>Cities</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h2>List of cities</h2>
<table>
<tr>
<th>Id</th>
<th>Name</th>
<th>Population</th>
</tr>
<#list cities as city>
<tr>
<td>${city.id}</td>
<td>${city.name}</td>
<td>${city.population}</td>
</tr>
</#list>
</table>
</body>
</html>
在showCities.ftl
模板文件中,我们在 HTML 表中显示数据。
resources/static/index.html
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<a href="showCitiesEnding?name=ou">Show cities ending in ou</a>
</body>
</html>
在index.html
中有一个链接,显示以ou
字符结尾的城市。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
$ mvn spring-boot:run
应用运行后,我们可以导航到localhost:8080/myapp/
。
在本教程中,我们展示了如何使用 Spring Data JPA @Query
注解创建自定义 JPQL 查询。 您可能也对相关教程感兴趣:
- Spring Boot MySQL 教程
- Spring Boot Data JPA
@NamedQuery
教程 - Spring Boot REST Data JPA 教程
- Spring Boot Data JPA 排序教程
- Spring Boot
CrudRepository
教程 - Spring Boot H2 REST 教程
Spring Boot Querydsl 教程
Spring Boot Querydsl 教程展示了如何使用 Querydsl 在 Spring Boot 应用中创建数据库查询。
Querydsl
Querydsl 是一个框架,可通过其流畅的 API 来构造静态类型的类似 SQL 的查询。 Spring Data 模块通过QuerydslPredicateExecutor
与 Querydsl 集成。
Spring Querydsl 示例
以下应用使用实体管理器和存储库使用 Querydsl 创建查询。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ ├───model
│ │ │ City.java
│ │ └───repository
│ │ CityRepository.java
│ └───resources
│ application.properties
│ data-h2.sql
│ schema-h2.sql
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootquerydsl</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<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>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>4.2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>4.2.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--Plugin for query-dsl-->
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Maven POM 文件包含 Spring Data JPA,H2 数据库和 Querydsl 的依赖项。 JPAAnnotationProcessor
查找带有Entity
注解的域类型,并为其生成查询类型。
注解处理是javac
中的一种工具,用于在编译时扫描和处理注解。
resources/application.properties
spring.main.banner-mode=off
spring.datasource.platform=h2
spring.jpa.hibernate.ddl-auto=none
logging.pattern.console=%d{dd-MM-yyyy HH:mm:ss} %magenta([%thread]) %highlight(%-5level) %logger.%M - %msg%n
application.properties
是主要的 Spring Boot 配置文件。 使用spring.main.banner-mode
属性,我们可以关闭 Spring 横幅。 spring.datasource.platform
设置数据库的供应商名称。 在初始化脚本中使用它。 最后,spring.jpa.hibernate.ddl-auto
禁止从实体自动创建模式。 logging.pattern.console
定义控制台的日志模式。
resources/schema-h2.sql
CREATE TABLE cities(id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255), population INT);
启动应用时,将执行schema-h2.sql
脚本。 它创建一个新的数据库表。
resources/data-h2.sql
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Suzhou', 4327066);
INSERT INTO cities(name, population) VALUES('Zhengzhou', 4122087);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
INSERT INTO cities(name, population) VALUES('Brest', 139163);
INSERT INTO cities(name, population) VALUES('Bucharest', 1836000);
之后,执行data-h2.sql
文件。 它用数据填充表。
com/zetcode/model/City.java
package com.zetcode.model;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "cities")
public class City {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int population;
public City() {
}
public City(String name, int population) {
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.id);
hash = 79 * hash + Objects.hashCode(this.name);
hash = 79 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append("City{id=").append(id).append(", name=")
.append(name).append(", population=")
.append(population).append("}");
return builder.toString();
}
}
这是City
实体。
com/zetcode/repository/CityRepository.java
package com.zetcode.repository;
import com.zetcode.model.City;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CityRepository extends CrudRepository<City, Long>,
QuerydslPredicateExecutor<City> {
}
要在我们的存储库中启用 Querydsl,我们从QuerydslPredicateExecutor
扩展。
com/zetcode/MyRunner.java
package com.zetcode;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQuery;
import com.zetcode.model.QCity;
import com.zetcode.repository.CityRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Component
@SuppressWarnings({ "rawtypes", "unchecked" })
public class MyRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);
@PersistenceContext
private EntityManager entityManager;
@Autowired
private CityRepository cityRepository;
@Override
public void run(String... args) throws Exception {
var qCity = QCity.city;
var query = new JPAQuery(entityManager);
query.from(qCity).where(qCity.name.eq("Bratislava")).distinct();
var c1 = query.fetch();
logger.info("{}", c1);
var query2 = new JPAQuery(entityManager);
query2.from(qCity).where(qCity.name.endsWith("est").and(qCity.population.lt(1800000)));
var cities = query2.fetch();
logger.info("{}", cities);
BooleanExpression booleanExpression = qCity.population.goe(2_000_000);
OrderSpecifier<String> orderSpecifier = qCity.name.asc();
var cities2 = cityRepository.findAll(booleanExpression, orderSpecifier);
logger.info("{}", cities2);
}
}
我们使用EntityManager
和CityRepository
创建 Querydsl 查询。
var qCity = QCity.city;
Querydsl 创建一个QCity
类型。
var query = new JPAQuery(entityManager);
在前两个查询中,我们使用实体管理器。
query.from(qCity).where(qCity.name.eq("Bratislava")).distinct();
var c1 = query.fetch();
logger.info("{}", c1);
使用 Querydsl 的流畅 API,我们获取了一个独特的城市对象。
var query2 = new JPAQuery(entityManager);
query2.from(qCity).where(qCity.name.endsWith("est").and(qCity.population.lt(1800000)));
var cities = query2.fetch();
logger.info("{}", cities);
一个更复杂的查询将获取多个城市。
BooleanExpression booleanExpression = qCity.population.goe(2_000_000);
OrderSpecifier<String> orderSpecifier = qCity.name.asc();
var cities2 = cityRepository.findAll(booleanExpression, orderSpecifier);
logger.info("{}", cities2);
在第三个查询中,我们利用存储库。
注意:在Java企业应用中,定义与存储库一起使用的服务层是一个好习惯。 为简单起见,我们跳过服务层。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
$ mvn spring-boot:run
...
04-06-2019 13:22:13 [main] INFO com.zetcode.MyRunner.run - [City{id=1, name=Bratislava, population=432000}]
04-06-2019 13:22:13 [main] INFO com.zetcode.MyRunner.run - [City{id=2, name=Budapest, population=1759000},
City{id=11, name=Brest, population=139163}]
04-06-2019 13:22:13 [main] INFO com.zetcode.MyRunner.run - [City{id=10, name=Berlin, population=3671000},
City{id=5, name=Los Angeles, population=3971000}, City{id=6, name=New York, population=8550000},
City{id=8, name=Suzhou, population=4327066}, City{id=9, name=Zhengzhou, population=4122087}]
...
我们运行该应用。
在本教程中,我们展示了如何使用 Querydsl 在 Spring Boot 应用中生成查询。
- Spring Boot
TestEntityManager
教程 - Spring Boot Data JPA
@NamedQuery
教程 - Spring Boot REST Data JPA 教程
- Spring Boot Data JPA 排序教程
- Spring Boot
CrudRepository
教程
Spring Boot Data JPA 排序教程
Spring Boot Data JPA 排序教程展示了如何在 Spring Data JPA 中对查询结果进行排序。 查询结果使用ORDER BY
子句或Sort
对象进行排序。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。
Spring Data JPA
Spring Data JPA 是伞式 Spring Data 项目的一部分,该项目使实现基于 JPA 的存储库更加容易。 Spring Data JPA 使用 JPA 将数据存储在关系数据库中。 它可以在运行时从存储库接口自动创建存储库实现。
Spring Data JPA 排序
在 Spring Data JPA 中,查询结果可以通过两种方式排序:
- 在 JPQL 查询中使用
ORDER BY
子句 - 将类型为
Sort
的参数添加到查询方法
Spring Boot Data JPA 排序示例
以下应用检索按升序排序的城市。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ ├───controller
│ │ │ MyController.java
│ │ ├───model
│ │ │ City.java
│ │ ├───repository
│ │ │ CityRepository.java
│ │ └───service
│ │ CityService.java
│ │ ICityService.java
│ └───resources
│ application.properties
│ data-h2.sql
│ schema-h2.sql
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootdatajpasort</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven pom.xml
文件。
resources/application.properties
spring.main.banner-mode=off
spring.datasource.platform=h2
spring.jpa.hibernate.ddl-auto=none
在主属性文件中,我们使用spring.main.banner-mode
属性关闭 Spring Boot 横幅。 spring.datasource.platform
设置为h2
,因此数据库名称存在于数据库初始化脚本中。 由于我们从 SQL 代码初始化脚本,因此通过将spring.jpa.hibernate.ddl-auto
设置为none
可以关闭从实体自动创建表的功能。
com/zetcode/model/City.java
package com.zetcode.model;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "cities")
public class City {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int population;
public City() {
}
public City(String name, int population) {
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.id);
hash = 79 * hash + Objects.hashCode(this.name);
hash = 79 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append("City{id=").append(id).append(", name=")
.append(name).append(", population=")
.append(population).append("}");
return builder.toString();
}
}
这是City
实体。
resources/schema-h2.sql
CREATE TABLE cities(id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255), population INT);
启动应用时,将执行schema-h2.sql
脚本。 它创建一个新的数据库表。
resources/data-h2.sql
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Suzhou', 4327066);
INSERT INTO cities(name, population) VALUES('Zhengzhou', 4122087);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
之后,执行data-h2.sql
文件。 它用数据填充表。
com/zetcode/service/ICityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import java.util.List;
public interface ICityService {
List<City> findAllOrderByPopulationAsc();
List<City> findAllOrderByNameAsc();
}
ICityService
包含两种签约方法。
com/zetcode/service/CityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import com.zetcode.repository.CityRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CityService implements ICityService {
@Autowired
private CityRepository repository;
@Override
public List<City> findAllOrderByPopulationAsc() {
return repository.findAllOrderByPopulationAsc();
}
@Override
public List<City> findAllOrderByNameAsc() {
var sort = new Sort(Sort.Direction.ASC, "name");
return repository.findAllOrderByNameAsc(sort);
}
}
在这里,我们有两种排序方法的实现。
@Override
public List<City> findAllOrderByNameAsc() {
var sort = new Sort(Sort.Direction.ASC, "name");
return repository.findAllOrderByNameAsc(sort);
}
Sort
对象按名称升序对查询结果进行排序。 Sort
作为参数传递给方法。
com/zetcode/repository/CityRepository.java
package com.zetcode.repository;
import com.zetcode.model.City;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface CityRepository extends CrudRepository<City, Long> {
@Query("FROM City ORDER BY population ASC")
List<City> findAllOrderByPopulationAsc();
@Query("FROM City")
List<City> findAllOrderByNameAsc(Sort sort);
}
CityRepository
有两种分类方法。 在第一种情况下,我们使用ORDER BY
子句。 在第二种情况下,我们使用Sort
对象。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import com.zetcode.model.City;
import com.zetcode.service.ICityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class MyController {
@Autowired
private ICityService cityService;
@GetMapping(value = "/cities")
public List<City> getCitiesByPopulation() {
return cityService.findAllOrderByPopulationAsc();
}
@GetMapping(value = "/cities2")
public List<City> getCitiesByName() {
return cityService.findAllOrderByNameAsc();
}
}
控制器为 RESTFul,并以 JSON 格式返回数据。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
是设置 Spring Boot 应用的入口点。
我们使用mvn -q spring-boot:run
运行该应用。
$ curl localhost:8080/cities
[{"id":1,"name":"Bratislava","population":432000},{"id":7,"name":"Edinburgh","population":464000},
{"id":3,"name":"Prague","population":1280000},{"id":4,"name":"Warsaw","population":1748000},
{"id":2,"name":"Budapest","population":1759000},{"id":10,"name":"Berlin","population":3671000},
{"id":5,"name":"LosAngeles","population":3971000},{"id":9,"name":"Zhengzhou","population":4122087},
{"id":8,"name":"Suzhou","population":4327066},{"id":6,"name":"NewYork","population":8550000}]
在此输出中,城市按人口按升序排序。
$ curl localhost:8080/cities2
[{"id":10,"name":"Berlin","population":3671000},{"id":1,"name":"Bratislava","population":432000},
{"id":2,"name":"Budapest","population":1759000},{"id":7,"name":"Edinburgh","population":464000},
{"id":5,"name":"LosAngeles","population":3971000},{"id":6,"name":"NewYork","population":8550000},
{"id":3,"name":"Prague","population":1280000},{"id":8,"name":"Suzhou","population":4327066},
{"id":4,"name":"Warsaw","population":1748000},{"id":9,"name":"Zhengzhou","population":4122087}]
在此输出中,城市按名称按升序排序。
本教程展示了如何在 Spring Boot Data JPA 中对查询结果进行排序。 您可能也对相关教程感兴趣: Spring Boot Data JPA @Query
教程, Spring Boot Data JPA @NamedQuery
教程, Java 教程或列出所有 Spring Boot 教程。
Spring Boot @DataJpaTest
教程
Spring Boot @DataJpaTest
教程显示了如何使用@DataJpaTest
注解测试 JPA 存储库。
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架的演进,可帮助您轻松创建独立的,生产级的基于 Spring 的应用。
@DataJpaTest
@DataJpaTest
用于测试 JPA 信息库。 与@RunWith(SpringRunner.class)
结合使用。 注解会禁用完全自动配置,并且仅应用与 JPA 测试相关的配置。 默认情况下,带有@DataJpaTest
注解的测试使用嵌入式内存数据库。
在我们的测试中,我们可以从应用中注入DataSource
,@JdbcTemplate
,@EntityManager
或任何 Spring Data 存储库。
包含所有这些组件(包括内存数据库)的应用上下文在所有用@DataJpaTest
注解的测试类中的所有测试方法之间共享。 因此,每个测试方法都在自己的事务中运行,该事务在方法执行后会回滚。 这样,测试就彼此保持独立。
Spring @DataJpaTest
示例
下面的应用创建一个自定义的 JPA 查询方法。 该方法在带有@DataJpaTest
注解的测试类中进行测试。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ ├───model
│ │ │ City.java
│ │ └───repository
│ │ CityRepository.java
│ └───resources
│ application.properties
│ data-h2.sql
│ schema-h2.sql
└───test
└───java
└───com
└───zetcode
└───repository
CityRepositoryTest.java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootdatajpatest</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<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-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Maven POM 文件包含 Spring Data JPA,测试和 H2 数据库的依赖项。
resources/application.properties
spring.main.banner-mode=off
spring.datasource.platform=h2
spring.jpa.hibernate.ddl-auto=none
application.properties
是主要的 Spring Boot 配置文件。 使用spring.main.banner-mode
属性,我们可以关闭 Spring 横幅。 spring.datasource.platform
设置数据库的供应商名称。 在初始化脚本中使用它。 最后,spring.jpa.hibernate.ddl-auto
禁止从实体自动创建模式。
resources/schema-h2.sql
CREATE TABLE cities(id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255), population INT);
启动应用时,将执行schema-h2.sql
脚本。 它创建一个新的数据库表。
resources/data-h2.sql
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Suzhou', 4327066);
INSERT INTO cities(name, population) VALUES('Zhengzhou', 4122087);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
INSERT INTO cities(name, population) VALUES('Brest', 139163);
INSERT INTO cities(name, population) VALUES('Bucharest', 1836000);
之后,执行data-h2.sql
文件。 它用数据填充表。
com/zetcode/model/City.java
package com.zetcode.model;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "cities")
public class City {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int population;
public City() {
}
public City(String name, int population) {
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.id);
hash = 79 * hash + Objects.hashCode(this.name);
hash = 79 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append("City{id=").append(id).append(", name=")
.append(name).append(", population=")
.append(population).append("}");
return builder.toString();
}
}
这是City
实体。
com/zetcode/repository/CityRepository.java
package com.zetcode.repository;
import com.zetcode.model.City;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface CityRepository extends CrudRepository<City, Long> {
@Query("SELECT c FROM City c WHERE c.name LIKE CONCAT('%',:ending, '%') AND c.population < :num")
List<City> findByNameEndingWithAndPopulationLessThan(@Param("ending") String ending,
@Param("num") Integer num);
}
CityRepository
包含自定义的findByNameEndingWithAndPopulationLessThan()
方法。 使用该方法,我们可以得到所有以指定字符串结尾且其人口小于指定值的城市名称。
com/zetcode/MyRunner.java
package com.zetcode;
import com.zetcode.repository.CityRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);
@Autowired
private CityRepository cityRepository;
@Override
public void run(String... args) throws Exception {
var cities = cityRepository.findByNameEndingWithAndPopulationLessThan("est", 1800000);
cities.forEach(city -> logger.info("{}", city));
}
}
在MyRunner
中,我们使用findByNameEndingWithAndPopulationLessThan()
方法。
注意:在Java企业应用中,定义与存储库一起使用的服务层是一个好习惯。 为简单起见,我们跳过服务层。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
com/zetcode/repository/CityRepositoryTest.java
package com.zetcode.repository;
import com.zetcode.model.City;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@DataJpaTest
public class CityRepositoryTest {
@Autowired
private CityRepository repository;
@Test
public void should_find_all_customers() {
Iterable<City> cities = repository.findAll();
int nOfCities = 12;
assertThat(cities).hasSize(nOfCities);
}
@Test
public void should_find_with_name_ending_population_less_than() {
var cities = repository.findByNameEndingWithAndPopulationLessThan("est", 150000);
assertThat(cities).isNotEmpty();
}
}
在CityRepositoryTest
中,我们测试了自定义 JPA 方法。
@RunWith(SpringRunner.class)
@DataJpaTest
public class CityRepositoryTest {
CityRepositoryTest
带有@DataJpaTest
注解。 内存中的 H2 数据库用于执行集成测试。
@Test
public void should_find_with_name_ending_population_less_than() {
var cities = repository.findByNameEndingWithAndPopulationLessThan("est", 150000);
assertThat(cities).isNotEmpty();
}
此方法测试至少有一个城市的名字以"est"
结尾并且人口少于 150000。
$ mvn spring-boot:test
我们运行测试。
在本教程中,我们展示了如何使用@DataJpaTest
测试自定义 JPA 存储库方法。
- Spring Boot
TestEntityManager
教程 - Spring Boot Data JPA
@NamedQuery
教程 - Spring Boot REST Data JPA 教程
- Spring Boot Data JPA 排序教程
- Spring Boot
CrudRepository
教程
Spring Boot TestEntityManager
教程
Spring Boot TestEntityManager
教程展示了如何在 JPA 测试中使用TestEntityManager
。 TestEntityManager
提供了EntityManager
方法的子集,可用于测试以及用于常见测试任务(例如persist
或find
)的辅助方法。
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架的演进,可帮助您轻松创建独立的,生产级的基于 Spring 的应用。
TestEntityManager
TestEntityManager
允许在测试中使用EntityManager
。 Spring Repository
是EntityManager
的抽象; 它使开发者免受 JPA 底层细节的困扰,并带来了许多便捷的方法。 但是 Spring 允许在应用代码和测试中需要时使用EntityManager
。
在我们的测试中,我们可以从应用中注入DataSource
,@JdbcTemplate
,@EntityManager
或任何 Spring Data 存储库。
Spring TestEntityManager
示例
以下应用使用TestEntityManager
以测试方法保存了一些城市实体。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ ├───model
│ │ │ City.java
│ │ └───repository
│ │ CityRepository.java
│ └───resources
└───test
└───java
└───com
└───zetcode
└───repository
CityRepositoryTest.java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springboottestentitymanager</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<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-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Maven POM 文件包含 Spring Data JPA,测试和 H2 数据库的依赖项。
com/zetcode/model/City.java
package com.zetcode.model;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "cities")
public class City {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int population;
public City() {
}
public City(String name, int population) {
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.id);
hash = 79 * hash + Objects.hashCode(this.name);
hash = 79 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append("City{id=").append(id).append(", name=")
.append(name).append(", population=")
.append(population).append("}");
return builder.toString();
}
}
这是City
实体。
com/zetcode/repository/CityRepository.java
package com.zetcode.repository;
import com.zetcode.model.City;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface CityRepository extends CrudRepository<City, Long> {
List<City> findByName(String name);
}
CityRepository
包含自定义的findByName()
方法。 Spring 检查方法的名称,并从其关键字派生查询。
com/zetcode/MyRunner.java
package com.zetcode;
import com.zetcode.model.City;
import com.zetcode.repository.CityRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);
@Autowired
private CityRepository cityRepository;
@Override
public void run(String... args) throws Exception {
logger.info("Saving cities");
cityRepository.save(new City("Bratislava", 432000));
cityRepository.save(new City("Budapest", 1759000));
cityRepository.save(new City("Prague", 1280000));
cityRepository.save(new City("Warsaw", 1748000));
logger.info("Retrieving cities");
var cities = cityRepository.findAll();
cities.forEach(city -> logger.info("{}", city));
}
}
在MyRunner
中,我们使用CityRepository
保存和检索实体。 数据存储在内存中的 H2 数据库中。
注意:在Java企业应用中,定义与存储库一起使用的服务层是一个好习惯。 为简单起见,我们跳过服务层。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
com/zetcode/repository/CityRepositoryTest.java
package com.zetcode.repository;
import com.zetcode.model.City;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
@RunWith(SpringRunner.class)
@DataJpaTest
public class CityRepositoryTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
private CityRepository repository;
@Test
public void testFindByName() {
entityManager.persist(new City("Bratislava", 432000));
entityManager.persist(new City("Budapest", 1759000));
entityManager.persist(new City("Prague", 1280000));
entityManager.persist(new City("Warsaw", 1748000));
var cities = repository.findByName("Bratislava");
assertEquals(1, cities.size());
assertThat(cities).extracting(City::getName).containsOnly("Bratislava");
}
}
在CityRepositoryTest
中,我们测试了自定义 JPA 方法。
@Autowired
private TestEntityManager entityManager;
我们注入TestEntityManager
。
@RunWith(SpringRunner.class)
@DataJpaTest
public class CityRepositoryTest {
@DataJpaTest
用于测试 JPA 信息库。 它与@RunWith(SpringRunner.class)
结合使用。 注解会禁用完全自动配置,并且仅应用与 JPA 测试相关的配置。 默认情况下,用@DataJpaTest
注解的测试使用嵌入式内存数据库。
entityManager.persist(new City("Bratislava", 432000));
entityManager.persist(new City("Budapest", 1759000));
entityManager.persist(new City("Prague", 1280000));
entityManager.persist(new City("Warsaw", 1748000));
我们用EntityManager
的persist()
方法保存了四个城市。
var cities = repository.findByName("Bratislava");
assertEquals(1, cities.size());
我们测试findByName()
方法返回一个城市。
assertThat(cities).extracting(City::getName).containsOnly("Bratislava");
在这里,我们测试城市的名称。
$ mvn spring-boot:test
我们运行测试。
在本教程中,我们在测试中使用了TestEntityManager
。
- Spring Boot
@DataJpaTest
教程 - Spring Boot Data JPA
@NamedQuery
教程 - Spring Boot REST Data JPA 教程
- Spring Boot Data JPA 排序教程
- Spring Boot
CrudRepository
教程
Spring Boot 发送电子邮件教程
Spring Boot 发送电子邮件教程展示了如何在 Spring Boot 应用中发送电子邮件。 我们使用 Mailtrap 服务。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。
Spring Boot 电子邮件示例
在下面的示例中,我们创建一个将电子邮件发送到 Mailtrap 帐户的应用。 如果没有帐户,我们需要注册一个帐户。 注册过程非常简单快捷。 有一个免费层,每月可发送 500 封电子邮件。
注意:Gmail 不是测试应用的理想选择。 我们应该使用诸如 Mailtrap 或 Mailgun 之类的在线服务,或者使用由网络托管公司提供的 SMTP 服务器。
该应用具有 Web 界面来发送电子邮件。 此外,可以通过测试发送电子邮件。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ ├───controller
│ │ │ MyController.java
│ │ └───service
│ │ EmailService.java
│ └───resources
│ │ application.properties
│ ├───static
│ │ index.html
│ └───templates
│ emailsent.ftl
└───test
└───java
└───com
└───zetcode
SendEmailApplicationTest.java
这是 Spring Boot 应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootemailex</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
我们在pom.xml
中具有项目依赖项。 对于电子邮件,我们需要声明spring-boot-starter-mail
。
resources/application.properties
spring.main.banner-mode=off
spring.mail.protocol=smtp
spring.mail.host=smtp.mailtrap.io
spring.mail.port=2525
spring.mail.username=0128491df14b6d
spring.mail.password=7b6d7ha59a1f08
spring.mail.properties.mail.smtp.auth = true
spring.mail.properties.mail.smtp.starttls.enable = true
我们为 Mailtrap 配置电子邮件设置。 这些详细信息在我们的 Mailtrap 帐户中提供。
com/zetcode/MyController.java
package com.zetcode.controller;
import com.zetcode.service.EmailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class MyController {
@Autowired
private EmailService emailService;
@GetMapping(value = "/sendmail")
public String sendmail() {
emailService.sendMail("kate@example.com", "Test Subject", "Test mail");
return "emailsent";
}
}
控制器包含一个发送电子邮件的映射。
com/zetcode/service/EmailService.java
package com.zetcode.service;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
private JavaMailSender javaMailSender;
public EmailService(JavaMailSender javaMailSender) {
this.javaMailSender = javaMailSender;
}
public void sendMail(String toEmail, String subject, String message) {
var mailMessage = new SimpleMailMessage();
mailMessage.setTo(toEmail);
mailMessage.setSubject(subject);
mailMessage.setText(message);
mailMessage.setFrom("johndoe@example.com");
javaMailSender.send(mailMessage);
}
}
电子邮件服务使用JavaMailSender
和SimpleMailMessage
发送简单的电子邮件。
resources/static/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Home page</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<a href="sendmail">Send mail</a>
</body>
</html>
index.html
文件是主页。 它包含一个用于发送电子邮件的锚。
resources/templates/emailsent.ftl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Email sent</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<p>
Email was sent
</p>
</body>
</html>
该模板包含一条简单的消息,该消息在成功发送电子邮件后显示。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
是设置 Spring Boot 应用的入口。
com/zetcode/SendEmailApplicationTest.java
package com.zetcode;
import com.zetcode.service.EmailService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SendEmailApplicationTest {
@Autowired
private EmailService emailService;
@Test
public void testEmail() {
emailService.sendMail("frank23@example.com", "Test subject", "Test mail");
}
}
这是发送电子邮件的测试。
$ mvn spring-boot:run
应用运行后,我们可以导航到localhost:8080
。
在本教程中,我们展示了如何在 Spring Boot 中发送电子邮件。 您可能也对相关教程感兴趣: Spring Boot Freemaker 教程, Java 教程或列出所有 Spring Boot 教程。
Spring Boot Data JPA 派生的查询
Spring Boot Data JPA 派生查询教程展示了如何从方法名称创建查询。
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架的演进,可帮助您轻松创建独立的,生产级的基于 Spring 的应用。
Spring Data JPA
Spring Data JPA 有助于实现基于 JPA 的存储库。 它增强了对基于 JPA 的数据访问层的支持。 它使构建使用数据访问技术的 Spring 支持的应用变得更加容易。 Spring Data JPA 是较大的 Spring Data 系列的一部分。
Spring Data JPA 派生的查询
Spring Data JPA 可以从方法名称创建查询。 这是约定而不是配置的特定形式。 Spring Data JPA 从具有属性组合的特定关键字创建查询; 例如:findByAgeLessThan
,findByFirstnameEndingWith
或findByFirstnameEquals
。 关键字列表在 Spring Data JPA 文档中提供。
Spring Boot Data JPA 派生查询示例
以下应用使用两个派生查询。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ ├───model
│ │ │ City.java
│ │ ├───repository
│ │ │ CityRepository.java
│ │ └───service
│ │ CityService.java
│ │ ICityService.java
│ └───resources
│ application.properties
│ data-h2.sql
│ schema-h2.sql
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootderivedqueries</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Maven POM 文件包含 H2 数据库和 Spring Boot Data JPA 的依赖项。
resources/application.properties
spring.main.banner-mode=off
spring.datasource.platform=h2
spring.jpa.hibernate.ddl-auto=none
在application.properties
文件中,我们编写了 Spring Boot 应用的各种配置设置。 使用spring.main.banner-mode
属性,我们可以关闭 Spring 横幅。
spring.datasource.platform
设置数据库的供应商名称。 在初始化脚本中使用它。 spring.jpa.hibernate.ddl-auto
禁止从实体自动创建模式。
com/zetcode/model/City.java
package com.zetcode.model;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "cities")
public class City {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int population;
public City() {
}
public City(String name, int population) {
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.id);
hash = 79 * hash + Objects.hashCode(this.name);
hash = 79 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append("City{id=").append(id).append(", name=")
.append(name).append(", population=")
.append(population).append("}");
return builder.toString();
}
}
这是City
实体。 每个实体必须至少定义两个注解:@Entity
和@Id
。
@Entity
@Table(name = "cities")
public class City {
@Entity
注解指定该类是一个实体,并映射到数据库表。 @Table
注解指定要用于映射的数据库表的名称。
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Id
注解指定实体的主键,@GeneratedValue
提供规范主键值的生成策略。
resources/schema-h2.sql
CREATE TABLE cities(id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255), population INT);
启动应用时,将执行schema-h2.sql
脚本。 它创建一个新的数据库表。
resources/data-h2.sql
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Brest', 139163);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Suzhou', 4327066);
INSERT INTO cities(name, population) VALUES('Zhengzhou', 4122087);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
INSERT INTO cities(name, population) VALUES('Bucharest', 1836000);
之后,执行data-h2.sql
文件。 它用数据填充表。
com/zetcode/repository/CityRepository.java
package com.zetcode.repository;
import com.zetcode.model.City;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface CityRepository extends CrudRepository<City, Long> {
List<City> findByNameEndingWith(String ending);
List<City> findByPopulationLessThan(int population);
}
我们有两种方法可以从中生成派生查询。
List<City> findByNameEndingWith(String ending);
在这里,查询将查找以指定字符串结尾的表单城市名称。
List<City> findByPopulationLessThan(int population);
查询将在其中查找人口少于指定数量的城市。
com/zetcode/service/ICityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import java.util.List;
public interface ICityService {
List<City> findByNameEndingWith(String ending);
List<City> findByPopulationLessThan(int population);
}
ICityService
提供了两种签约方法。
com/zetcode/service/CityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import com.zetcode.repository.CityRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CityService implements ICityService {
@Autowired
private CityRepository cityRepository;
@Override
public List<City> findByNameEndingWith(String ending) {
return cityRepository.findByNameEndingWith(ending);
}
@Override
public List<City> findByPopulationLessThan(int population) {
return cityRepository.findByPopulationLessThan(population);
}
}
CityService
包含服务方法实现。 派生的查询在cityRepository
上调用。
com/zetcode/MyRunner.java
package com.zetcode;
import com.zetcode.service.ICityService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);
@Autowired
private ICityService cityService;
@Override
public void run(String... args) throws Exception {
logger.info("Finding cities having population less than one million");
var res1 = cityService.findByPopulationLessThan(1000000);
logger.info("{}", res1);
logger.info("Finding cities by name ending with 'est'");
var res2 = cityService.findByNameEndingWith("est");
logger.info("{}", res2);
}
}
MyRunner
获取所有人口少于一百万的城市以及所有名称以"est"
结尾的城市。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
$ mvn spring-boot:run
...
19-05-21 Tue 14:53:39.892 INFO MyRunner [City{id=1, name=Bratislava, population=432000},
City{id=7, name=Brest, population=139163}, City{id=8, name=Edinburgh, population=464000}]
19-05-21 Tue 14:53:39.894 INFO MyRunner Finding cities by name ending with 'est'
19-05-21 Tue 14:53:39.903 INFO MyRunner [City{id=2, name=Budapest, population=1759000},
City{id=7, name=Brest, population=139163}, City{id=12, name=Bucharest, population=1836000}]
...
我们运行该应用。
在本教程中,我们使用了 Spring Data JPA 派生的查询来获取数据。 您可能也对相关教程感兴趣:
- Spring Boot Data JPA
@NamedQuery
教程 - Spring Boot REST Data JPA 教程
- Spring Boot Data JPA 排序教程
- Spring Boot
CrudRepository
教程
Spring Boot Data JPA 示例查询
Spring Boot Data JPA 示例查询教程展示了如何使用 Spring Data JPA 示例查询技术创建查询。
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架的演进,可帮助您轻松创建独立的,生产级的基于 Spring 的应用。
Spring Data JPA
Spring Data JPA 有助于实现基于 JPA 的存储库。 它增强了对基于 JPA 的数据访问层的支持。 它使构建使用数据访问技术的 Spring 支持的应用变得更加容易。 Spring Data JPA 是较大的 Spring Data 系列的一部分。
Spring Data JPA 查询示例
示例查询(QBE)是一种具有简单接口的用户友好查询技术。 它允许动态查询创建。 我们不需要使用商店特定的查询语言编写查询。
我们处理三个对象。 probe
是带有填充字段的域对象的实际示例。 ExampleMatcher
包含有关如何匹配特定字段的详细信息。 Example
由探针和ExampleMatcher
组成。 它用于创建查询。
QBE 有一些局限性。 它无法创建一些更高级的查询。
Spring Boot Data JPA QBE 示例
以下应用使用 QBE 生成查询。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ ├───model
│ │ │ City.java
│ │ ├───repository
│ │ │ CityRepository.java
│ │ └───service
│ │ CityService.java
│ │ ICityService.java
│ └───resources
│ application.properties
│ data-h2.sql
│ schema-h2.sql
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootquerybyexample</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Maven POM 文件包含 H2 数据库和 Spring Boot Data JPA 的依赖项。
resources/application.properties
spring.main.banner-mode=off
spring.datasource.platform=h2
spring.jpa.hibernate.ddl-auto=none
在application.properties
文件中,我们编写了 Spring Boot 应用的各种配置设置。 使用spring.main.banner-mode
属性,我们可以关闭 Spring 横幅。
spring.datasource.platform
设置数据库的供应商名称。 在初始化脚本中使用它。 spring.jpa.hibernate.ddl-auto
禁止从实体自动创建模式。
com/zetcode/model/City.java
package com.zetcode.model;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "cities")
public class City {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int population;
public City() {
}
public City(String name, int population) {
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.id);
hash = 79 * hash + Objects.hashCode(this.name);
hash = 79 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append("City{id=").append(id).append(", name=")
.append(name).append(", population=")
.append(population).append("}");
return builder.toString();
}
}
这是City
实体。 每个实体必须至少定义两个注解:@Entity
和@Id
。
resources/schema-h2.sql
CREATE TABLE cities(id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255), population INT);
启动应用时,将执行schema-h2.sql
脚本。 它创建一个新的数据库表。
resources/data-h2.sql
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Brest', 139163);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Suzhou', 4327066);
INSERT INTO cities(name, population) VALUES('Zhengzhou', 4122087);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
INSERT INTO cities(name, population) VALUES('Bucharest', 1836000);
之后,执行data-h2.sql
文件。 它用数据填充表。
com/zetcode/repository/CityRepository.java
package com.zetcode.repository;
import com.zetcode.model.City;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;
import org.springframework.stereotype.Repository;
@Repository
public interface CityRepository extends CrudRepository<City, Long>,
QueryByExampleExecutor<City> {
}
我们通过从QueryByExampleExecutor
扩展存储库来启用 QBE API。
com/zetcode/service/ICityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import java.util.List;
public interface ICityService {
List<City> findByNameEnding(String ending);
List<City> findByName(String name);
}
ICityService
提供了两种签约方法。
com/zetcode/service/CityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import com.zetcode.repository.CityRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.stereotype.Service;
import java.util.List;
import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.exact;
@Service
public class CityService implements ICityService {
@Autowired
private CityRepository cityRepository;
@Override
public List<City> findByNameEnding(String ending) {
var city = new City();
city.setName(ending);
var matcher = ExampleMatcher.matching()
.withMatcher("name", match -> match.endsWith())
.withIgnorePaths("population");
var example = Example.of(city, matcher);
return (List<City>) cityRepository.findAll(example);
}
@Override
public List<City> findByName(String name) {
var city = new City();
city.setName(name);
var matcher = ExampleMatcher.matching()
.withMatcher("name", exact())
.withIgnorePaths("population");
var example = Example.of(city, matcher);
return (List<City>) cityRepository.findAll(example);
}
}
CityService
包含服务方法实现。
var city = new City();
city.setName(ending);
我们有一个City
域对象。 这称为探针。
var matcher = ExampleMatcher.matching()
.withMatcher("name", match -> match.endsWith())
.withIgnorePaths("population");
匹配器将以城市名称结尾的字符串匹配,并忽略人口。
var example = Example.of(city, matcher);
根据域对象和匹配器创建一个Example
。
return (List<City>) cityRepository.findAll(example);
Example
传递给findAll()
方法。
com/zetcode/MyRunner.java
package com.zetcode;
import com.zetcode.service.ICityService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);
@Autowired
private ICityService cityService;
@Override
public void run(String... args) throws Exception {
logger.info("Finding cities by name");
var res1 = cityService.findByName("Bratislava");
logger.info("{}", res1);
var res2 = cityService.findByName("Berlin");
logger.info("{}", res2);
logger.info("Finding cities by name ending with est");
var res3 = cityService.findByNameEnding("est");
logger.info("{}", res3);
}
}
MyRunner
查找"Bratislava"
和"Berlin"
城市,并找到所有名称以"est"
结尾的城市。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
$ mvn spring-boot:run
...
2019-05-21 16:24:33.480 com.zetcode.MyRunner Finding cities by name
2019-05-21 16:24:33.771 com.zetcode.MyRunner [City{id=1, name=Bratislava, population=432000}]
2019-05-21 16:24:33.773 com.zetcode.MyRunner [City{id=11, name=Berlin, population=3671000}]
2019-05-21 16:24:33.774 com.zetcode.MyRunner Finding cities by name ending with est
2019-05-21 16:24:33.781 com.zetcode.MyRunner [City{id=2, name=Budapest, population=1759000},
City{id=7, name=Brest, population=139163}, City{id=12, name=Bucharest, population=1836000}]
...
我们运行该应用。
在本教程中,我们使用了 Spring Data JPA 示例查询技术来生成查询。 您可能也对相关教程感兴趣:
- Spring Boot Data JPA
@NamedQuery
教程 - Spring Boot Data JPA 派生查询教程
- Spring Boot Data JPA 排序教程
- Spring Boot
CrudRepository
教程
Spring Boot Jersey 教程
Spring Boot Jersey 教程展示了如何在 Spring Boot 应用中使用 Jersey 建立一个简单的 RESTFul 应用。 Jersey 是使用@RestController
创建的 Spring RESTFul 应用的替代方案。
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架发展的下一步。 它有助于以最小的努力创建独立的,基于生产级的 Spring 应用。 它提倡在 XML 配置上使用约定而不是配置原则。
RESTFul 应用
RESTFul 应用遵循 REST 架构样式,该样式用于设计网络应用。 RESTful 应用生成对资源执行 CRUD(创建/读取/更新/删除)操作的 HTTP 请求。 RESTFul 应用通常以 JSON 或 XML 格式返回数据。
JAX-RS
RESTful Web 服务的 Java API(JAX-RS)是 Java 编程语言 API 规范,它提供了根据代表性状态传输(REST)架构模式创建 Web 服务的支持。 JAX-RS 使用注解来简化 Web 服务客户端和端点的开发和部署。 JAX-RS 是 Java EE 的正式组成部分。
Jersey
Jersey 是用于用 Java 开发 RESTful Web 服务的开源框架。 它是 RESTful Web 服务 Java API(JAX-RS)规范的参考实现。
Spring Boot Jersey
示例
以下应用是使用 Jersey 创建的 simpe Spring Boot RESTful 应用。
$ tree
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ ├── config
│ │ │ └── JerseyConfig.java
│ │ └── endpoint
│ │ ├── HelloEndpoint.java
│ │ └── ReverseReturnEndpoint.java
│ └── resources
└── test
└── java
└── com
└── zetcode
└── ApplicationTests.java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>SpringBootJersey</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 Spring Boot 启动器是一组方便的依赖项描述符,可以极大地简化 Maven 配置。 spring-boot-starter-parent
具有 Spring Boot 应用的一些常用配置。 spring-boot-starter-jersey
是使用 JAX-RS 和 Jersey 构建 RESTful Web 应用的入门。 它是spring-boot-starter-web
的替代方法。 spring-boot-starter-test
是使用包含 JUnit,Hamcrest 和 Mockito 的库测试 Spring Boot 应用的入门程序。
spring-boot-maven-plugin
在 Maven 中提供了 Spring Boot 支持,使我们可以打包可执行的 JAR 或 WAR 档案。 它的spring-boot:run
目标运行 Spring Boot 应用。
application.yml
server:
port: 8086
context-path: /api
spring:
main:
banner-mode: "off"
logging:
level:
org:
springframework: ERROR
在application.yml
文件中,我们编写了 Spring Boot 应用的各种配置设置。 我们设置端口和上下文路径。 使用banner-mode
属性,我们可以关闭 Spring 横幅。
我们将 spring 框架的日志记录级别设置为ERROR
。 在application.yml
文件位于中src/main/resources
目录。
JerseyConfig.java
package com.zetcode.config;
import com.zetcode.endpoint.HelloService;
import com.zetcode.endpoint.ReverseService;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(HelloService.class);
register(ReverseService.class);
}
}
JerseyConfig
注册两个服务类别。
HelloService.java
package com.zetcode.service;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import org.springframework.stereotype.Service;
@Service
@Path("/hello")
public class HelloService {
@GET
@Produces("text/plain")
public String hello() {
return "Hello from Spring";
}
}
这是HelloService
。 @Path
注解定义了服务类将响应的 URL。 Spring 的@Service
也注解了HelloService
以进行自动检测。 我们的服务方法仅返回"Hello from Spring"
消息。
HelloService.java
package com.zetcode.service;
import javax.validation.constraints.NotNull;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import org.springframework.stereotype.Service;
@Service
@Path("/reverse")
public class ReverseService {
@GET
@Produces("text/plain")
public String reverse(@QueryParam("data") @NotNull String data) {
return new StringBuilder(data).reverse().toString();
}
}
reverse()
服务方法返回一个反向的字符串。 它接受一个参数,不能为 null。 @QueryParam
将 HTTP 查询参数的值绑定到资源方法参数。
ApplicationTests.java
package com.zetcode;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void hello() {
ResponseEntity<String> entity = this.restTemplate.getForEntity("/hello",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).isEqualTo("Hello from Spring");
}
@Test
public void reverse() {
ResponseEntity<String> entity = this.restTemplate
.getForEntity("/reverse?data=regit", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).isEqualTo("tiger");
}
@Test
public void validation() {
ResponseEntity<String> entity = this.restTemplate.getForEntity("/reverse",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
}
}
在ApplicationTests
中,我们测试了两个端点。
Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
$ mvn spring-boot:run
使用mvn spring-boot:run
命令,运行应用。 该应用部署在嵌入式 Tomcat 服务器上。
$ curl localhost:8086/api/hello
Hello from Spring
使用curl
命令,我们连接到 hello 端点。
$ curl localhost:8086/api/reverse?data=summer
remmus
夏天的字符是相反的。
在本教程中,我们使用 Jersey 借助 Spring Boot 创建了一个简单的 RESTFul 应用,它是 JAX-RS 规范的参考实现。 您可能也对相关教程感兴趣:
- Spring Boot REST XML 教程
- 带有嵌入式 Jetty 的 Jersey 应用
- Spring Boot H2 REST 教程
- Spring Boot Thymeleaf 教程
- Jersey 应用中的 Web URL
- Spring Web 应用介绍
- Spring Boot RESTFul 应用
Spring Boot CSV 教程
在本教程中,我们将在 Spring Boot RESTful 应用中以 CSV 格式提供内容。 我们使用 OpenCSV 库。
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架发展的下一步。 它有助于以最小的努力创建独立的,基于生产级的 Spring 应用。 它放弃了 XML 配置,并大量使用约定而不是配置原则。
CSV(逗号分隔值)是一种简单的数据格式,其中(大多数)值之间用逗号分隔,每一行代表一个记录。 数据存储在纯文本文件中。 作为电子表格和数据库中使用的导入和导出格式,它非常流行。 OpenCSV 是 Java 的开源,简单 CSV 解析器库。
Hibernate 是 Java 语言的对象关系映射框架。 它提供了一个框架,用于将面向对象的域模型映射到关系数据库。 对象关系映射(ORM)是一种编程技术,用于在面向对象的编程语言中的不兼容类型系统之间转换数据。
Spring Data JPA 是 Spring Data 项目的一部分,该项目使实现基于 JPA 的存储库变得更加容易。 Spring Data JPA 使用 JPA 将数据存储在关系数据库中。 它可以在运行时从存储库接口自动创建存储库实现。
RESTFul 应用遵循 REST 架构样式,该样式用于设计网络应用。 RESTful 应用生成 HTTP 请求,这些请求对资源执行 CRUD(创建/读取/更新/删除)操作。
Spring Boot CSV 示例
我们的应用是一个 Spring Boot RESTful 应用,它以 CSV 格式从 H2 数据库返回数据。
$ tree
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ ├── bean
│ │ │ └── City.java
│ │ ├── controller
│ │ │ └── MyCsvController.java
│ │ ├── repository
│ │ │ └── CityRepository.java
│ │ ├── service
│ │ │ ├── CityService.java
│ │ │ └── ICityService.java
│ │ └── util
│ │ └── WriteCsvToResponse.java
│ └── resources
│ ├── application.yml
│ └── import.sql
└── test
└── java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>SpringBootCSV</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 h2
依赖项添加了一个 H2 数据库驱动程序。 opencsv
依赖项添加了 OpenCSV 库的驱动程序。
Spring Boot 启动器是一组方便的依赖项描述符,我们可以在我们的应用中包含这些描述符。 它们极大地简化了 Maven 配置。 spring-boot-starter-parent
为 Spring Boot 应用提供了一些常用配置。 spring-boot-starter-web
是使用 Spring MVC 构建 Web(包括 RESTful)应用的入门工具。 它使用 Tomcat 作为默认的嵌入式容器。 spring-boot-starter-data-jpa
是将 Spring Data JPA 与 Hibernate 结合使用的入门工具。
在spring-boot-maven-plugin
提供了 Maven 的 Spring Boot 支持,使我们能够打包可执行的 JAR 或 WAR 档案。 它的spring-boot:run
目标运行 Spring Boot 应用。
application.yml
server:
port: 8086
context-path: /rest
spring:
main:
banner-mode: "off"
jpa:
database: h2
hibernate:
dialect: org.hibernate.dialect.H2Dialect
ddl-auto: create-drop
logging:
level:
org:
springframework: ERROR
application.yml
文件包含 Spring Boot 应用的各种配置设置。 我们具有服务器端口和上下文路径(应用名称)的映射。 我们通过localhost:8086/rest/
URL 访问我们的应用。 使用banner-mode
属性,我们可以关闭 Spring 横幅。
JPA database
值指定要操作的目标数据库。 在本例中,我们指定了 Hibernate 方言org.hibernate.dialect.H2Dialect
。 ddl-auto
是数据定义语言模式; create-drop
选项将自动创建和删除数据库模式。
H2 数据库在内存中运行。 另外,我们将 spring 框架的日志记录级别设置为ERROR
。 在application.yml
文件位于中src/main/resources
目录。
City.java
package com.zetcode.bean;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "CITIES")
public class City implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int population;
public City() {
}
public City(Long id, String name, int population) {
this.id = id;
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public String toString() {
return "City{" + "id=" + id + ", name=" + name
+ ", population=" + population + '}';
}
}
这是City
实体。 每个实体必须至少定义两件事:@Entity
注解和带有@Id
注解的 ID 字段。 我们已经将ddl-auto
选项设置为create-drop
,这意味着 Hibernate 将根据该实体创建表模式。
@Entity
@Table(name = "CITIES")
public class City implements Serializable {
@Entity
注解指定该类是一个实体,并映射到数据库表。 @Table
实体指定要用于映射的数据库表的名称。
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Id
注解指定实体的主键,@GeneratedValue
提供规范主键值的生成策略。
import.sql
INSERT INTO CITIES(NAME, POPULATION) VALUES('Bratislava', 432000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Budapest', 1759000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Prague', 1280000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Warsaw', 1748000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Los Angeles', 3971000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('New York', 8550000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Edinburgh', 464000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Berlin', 3671000);
模式是由 Hibernate 自动创建的。 之后,将执行import.sql
文件以将数据填充到表中。
CityRepository.java
package com.zetcode.repository;
import com.zetcode.bean.City;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CityRepository extends CrudRepository<City, Long> {
}
通过从 Spring CrudRepository
扩展,我们将为我们的数据存储库实现一些方法,包括findAll()
和findOne()
。 这样,我们节省了大量样板代码。
ICityService.java
package com.zetcode.service;
import com.zetcode.bean.City;
import java.util.List;
public interface ICityService {
public List<City> findAll();
public City findById(Long id);
}
ICityService
提供了获取所有城市并通过其 ID 从数据源获取城市的契约方法。
CityService.java
package com.zetcode.service;
import com.zetcode.bean.City;
import com.zetcode.repository.CityRepository;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CityService implements ICityService {
@Autowired
private CityRepository repository;
@Override
public List<City> findAll() {
List<City> cities = (List<City>) repository.findAll();
return cities;
}
@Override
public City findById(Long id) {
City city = repository.findOne(id);
return city;
}
}
CityService
包含findAll()
和findById()
方法的实现。 我们使用存储库从数据库检索数据。
@Autowired
private CityRepository repository;
注入CityRepository
。
List<City> cities = (List<City>) repository.findAll();
存储库的findAll()
方法返回城市列表。
City city = repository.findOne(id);
存储库的findOne()
方法返回一个特定的城市对象。
MyCsvController.java
package com.zetcode.controller;
import com.zetcode.bean.City;
import com.zetcode.service.ICityService;
import com.zetcode.util.WriteCsvToResponse;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyCsvController {
@Autowired
ICityService cityService;
@RequestMapping(value = "/cities", produces = "text/csv")
public void findCities(HttpServletResponse response) throws IOException {
List<City> cities = (List<City>) cityService.findAll();
WriteCsvToResponse.writeCities(response.getWriter(), cities);
}
@RequestMapping(value = "/cities/{cityId}", produces = "text/csv")
public void findCity(@PathVariable Long cityId, HttpServletResponse response) throws IOException {
City city = cityService.findById(cityId);
WriteCsvToResponse.writeCity(response.getWriter(), city);
}
}
这是 Spring Boot RESTful 应用的控制器类。 @RestController
注解创建一个 RESTful 控制器。 传统的 MVC 控制器使用ModelAndView
,而 RESTful 控制器仅返回对象,并且对象数据通常以 JSON 或 XML 格式直接写入 HTTP 响应(通常)。 在本例中,我们选择了 CSV 格式。
@Autowired
private ICityService cityService;
我们在countryService
字段中插入ICityService
。
@RequestMapping(value = "/cities", produces = "text/csv")
public void findCities(HttpServletResponse response) throws IOException {
...
}
@RequestMapping
注解用于将 Web 请求映射到 Spring 控制器方法。 produces
选项设置媒体类型,在本例中为text/csv
。 我们将带有/cities
路径的请求映射到控制器的findCities()
方法。 默认请求是 GET 请求。
List<City> cities = (List<City>) cityService.findAll();
WriteCsvToResponse.writeCities(response.getWriter(), cities);
我们称cityService
的findAll()
来获取所有城市。 我们将 CSV 数据写入HttpServletResponse
对象。 Java Bean 与 CSV 数据的映射委托给WriteCsvToResponse
类。
@RequestMapping(value = "/cities/{cityId}", produces = "text/csv")
public void findCity(@PathVariable Long cityId, HttpServletResponse response) throws IOException {
City city = cityService.findById(cityId);
WriteCsvToResponse.writeCity(response.getWriter(), city);
}
在第二种方法中,我们有一个 URL 路径,其中包含要检索的城市的 ID。 我们使用@PathVariable
注解将 URL 模板变量绑定到方法cityId
参数。
WriteCsvToResponse.java
package com.zetcode.util;
import com.opencsv.CSVWriter;
import com.opencsv.bean.ColumnPositionMappingStrategy;
import com.opencsv.bean.StatefulBeanToCsv;
import com.opencsv.bean.StatefulBeanToCsvBuilder;
import com.opencsv.exceptions.CsvException;
import com.zetcode.bean.City;
import java.io.PrintWriter;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WriteCsvToResponse {
private static final Logger LOGGER = LoggerFactory.getLogger(WriteCsvToResponse.class);
public static void writeCities(PrintWriter writer, List<City> cities) {
try {
ColumnPositionMappingStrategy mapStrategy
= new ColumnPositionMappingStrategy();
mapStrategy.setType(City.class);
mapStrategy.generateHeader();
String[] columns = new String[]{"id", "name", "population"};
mapStrategy.setColumnMapping(columns);
StatefulBeanToCsv btcsv = new StatefulBeanToCsvBuilder(writer)
.withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
.withMappingStrategy(mapStrategy)
.withSeparator(',')
.build();
btcsv.write(cities);
} catch (CsvException ex) {
LOGGER.error("Error mapping Bean to CSV", ex);
}
}
public static void writeCity(PrintWriter writer, City city) {
try {
ColumnPositionMappingStrategy mapStrategy
= new ColumnPositionMappingStrategy();
mapStrategy.setType(City.class);
String[] columns = new String[]{"id", "name", "population"};
mapStrategy.setColumnMapping(columns);
StatefulBeanToCsv btcsv = new StatefulBeanToCsvBuilder(writer)
.withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
.withMappingStrategy(mapStrategy)
.withSeparator(',')
.build();
btcsv.write(city);
} catch (CsvException ex) {
LOGGER.error("Error mapping Bean to CSV", ex);
}
}
}
在WriteCsvToResponse
中,我们使用 OpenCSV 库将 Java bean 转换为 CSV,并将最终输出写入HttpServletResponse
中。
ColumnPositionMappingStrategy mapStrategy
= new ColumnPositionMappingStrategy();
mapStrategy.setType(City.class);
MappingStrategy
定义如何将 Java 属性映射到 CSV 列名称。 ColumnPositionMappingStrategy
使用列位置进行映射。
String[] columns = new String[]{"id", "name", "population"};
mapStrategy.setColumnMapping(columns);
我们设置列名。
StatefulBeanToCsv btcsv = new StatefulBeanToCsvBuilder(writer)
.withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
.withMappingStrategy(mapStrategy)
.withSeparator(',')
.build();
StatefulBeanToCsv
类以 CSV 格式写出 bean 给写程序,以保留状态信息并明智地猜测要应用的映射策略。
btcsv.write(city);
最后,编写了 bean。
Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
$ mvn spring-boot:run
使用mvn spring-boot:run
命令,运行应用。 该应用部署在嵌入式 Tomcat 服务器上。
$ curl localhost:8086/rest/cities
1,Bratislava,432000
2,Budapest,1759000
3,Prague,1280000
4,Warsaw,1748000
5,Los Angeles,3971000
6,New York,8550000
7,Edinburgh,464000
8,Berlin,3671000
使用curl
命令,我们可以获得所有城市。
$ curl localhost:8086/rest/cities/1
1,Bratislava,432000
在这里,我们得到了一个由其 ID 标识的城市。
在本教程中,我们已从 Spring Boot RESTful 应用以 CSV 格式将数据返回给客户端。 我们使用了 OpenCSV 库。 您可能也对相关教程感兴趣:
- 在 Spring Boot 中加载资源
- Spring Boot H2 REST 教程
- Spring Boot Thymeleaf 教程
- Spring Boot Mustache 教程
- Spring Boot Swing 集成教程
- Spring Web 应用介绍
- Spring Boot RESTFul 应用
- 经典 Spring 应用中的
JdbcTemplate
SpringBootServletInitializer
教程
原文: http://zetcode.com/springboot/springbootservletinitializer/
SpringBootServletInitializer
教程展示了如何从传统的 WAR 部署中部署 Spring Boot 应用。
当前的趋势是从可执行的 JAR 部署 Spring Boot 应用。 (有关如何从 JAR 启动简单的 Web 应用的详细信息,请参见 Spring Boot 第一个 Web 应用。)
Spring 是流行的 Java 应用框架。 Spring Boot 致力于以最小的努力创建独立的,基于生产级别的基于 Spring 的应用。
SpringBootServletInitializer
SpringBootServletInitializer
是从传统 WAR 部署运行SpringApplication
的接口。 它将 Servlet,Filter 和ServletContextInitializer
Bean 从应用上下文绑定到服务器。
SpringBootServletInitializer 示例
该应用创建一个简单的 Spring Boot RESTful 应用并将其打包到 WAR 中。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ └───controller
│ │ MyController.java
│ └───resources
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>servletinitializer</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 spring-boot-starter-web
是使用 Spring MVC 构建 Web(包括 RESTful)应用的入门程序。
该应用打包到一个 WAR 文件中。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@GetMapping(value = "/", produces = MediaType.TEXT_PLAIN_VALUE)
public String index() {
return "Hello there";
}
}
这是 Spring Boot Web 应用的控制器类。 控制器以@Restontroller
注解修饰。
@GetMapping(value = "/", produces = MediaType.TEXT_PLAIN_VALUE)
public String index() {
return "Hello there";
}
对主页的 GET 请求返回一个字符串。 绑定是通过@GetMapping
完成的。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 它从SpringBootServletInitializer
扩展而来,可以将其部署为 WAR。
通过将 WAR 部署在 Tomcat 服务器上并将其作为具有嵌入式 Tomcat 的自可执行 Web 归档执行,可以运行该应用。
在本教程中,我们创建了第一个可从传统 WAR 部署的 Spring Boot Web 应用。 您可能也对相关教程感兴趣: Spring Boot 第一个 Web 应用, Spring Web 应用简介,独立的 Spring 应用, [FreeMarker 教程 , Java 教程,游戏简介, Spark 简介或 Strips 简介。
在 Spring Boot 中加载资源
在本教程中,我们将展示如何在 Spring Boot 应用中加载资源。
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是一种以最少的精力创建独立的,基于生产级别的基于 Spring 的应用的方法。
Spring Boot 资源
资源是程序需要以与程序代码的位置无关的方式访问的数据,例如图像,音频和文本。
由于java.net.URL
不足以处理各种低级资源,因此 Spring 引入了org.springframework.core.io.Resource
。 要访问资源,我们可以使用@Value
注解或ResourceLoader
类。
Spring Boot 加载资源示例
我们的应用是一个 Spring Boot 命令行应用,它可以计算文本文件中单词的出现次数。 该文件位于src/main/resources
目录中,这是应用资源的标准 Maven 位置。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ └───service
│ │ CountWords.java
│ └───resources
│ application.yaml
│ thermopylae.txt
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootresource</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 Spring Boot 启动器是一组方便的依赖项描述符,我们可以在我们的应用中包含这些描述符。 它们极大地简化了 Maven 配置。 spring-boot-starter-parent
提供了 Spring Boot 应用的一些常见配置。 spring-boot-starter
依赖项是一个核心启动器,其中包括自动配置支持,日志记录和 YAML。 spring-boot-maven-plugin
在 Maven 中提供了 Spring Boot 支持,使我们可以打包可执行的 JAR 或 WAR 档案。 它的spring-boot:run
目标运行 Spring Boot 应用。
resources/application.yml
spring:
main:
banner-mode: "off"
logging:
level:
org:
springframework: ERROR
com:
zetcode: INFO
application.yml
文件包含 Spring Boot 应用的各种配置设置。 我们具有banner-mode
属性,可在其中关闭 Spring 横幅。 另外,我们将 spring 框架的日志记录级别设置为ERROR
,将我们的应用设置为 INFO。 该文件位于src/main/resources
目录中。
resources/thermopylae.txt
The Battle of Thermopylae was fought between an alliance of Greek city-states,
led by King Leonidas of Sparta, and the Persian Empire of Xerxes I over the
course of three days, during the second Persian invasion of Greece.
It took place simultaneously with the naval battle at Artemisium, in August
or September 480 BC, at the narrow coastal pass of Thermopylae.
The Persian invasion was a delayed response to the defeat of the first Persian
invasion of Greece, which had been ended by the Athenian victory at the Battle
of Marathon in 490 BC. Xerxes had amassed a huge army and navy, and set out to
conquer all of Greece.
这是我们在应用中读取的文本文件。 它也位于src/main/resources
目录中。
com/zetcode/service/CountWords.java
package com.zetcode.service;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
@Component
public class CountWords {
public Map<String, Integer> getWordsCount(Resource res) throws IOException {
Map<String, Integer> wordCount = new HashMap<>();
List<String> lines = Files.readAllLines(Paths.get(res.getURI()),
StandardCharsets.UTF_8);
for (String line : lines) {
String[] words = line.split("\\s+");
for (String word : words) {
if (word.endsWith(".") || word.endsWith(",")) {
word = word.substring(0, word.length() - 1);
}
if (wordCount.containsKey(word)) {
wordCount.put(word, wordCount.get(word) + 1);
} else {
wordCount.put(word, 1);
}
}
}
return wordCount;
}
}
CountWords
是一个 Spring 托管的 bean,它执行给定文件中的单词计数。 文本从文件中读取到句子列表中。 句子被分成单词并计数。
Map<String, Integer> wordCount = new HashMap<>();
wordCount
是一个映射,其中键是单词,频率是整数。
List<String> lines = Files.readAllLines(Paths.get(res.getURI()),
StandardCharsets.UTF_8);
我们使用Files.readAllLines()
方法一次读取所有内容。 Files.readAllLines()
方法返回字符串列表。
for (String line : lines) {
String[] words = line.split(" ");
...
我们遍历这些线,将它们分成单词; 单词之间用空格隔开。
if (word.endsWith(".") || word.endsWith(",")) {
word = word.substring(0, word.length()-1);
}
我们删除尾随点和逗号。
if (wordCount.containsKey(word)) {
wordCount.put(word, wordCount.get(word) + 1);
} else {
wordCount.put(word, 1);
}
如果单词已经在映射中,则增加其频率; 否则,我们将其插入映射并将其频率设置为 1。
com/zetcode/MyRunner.java
package com.zetcode;
import com.zetcode.count.CountWords;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
@Value("classpath:thermopylae.txt")
private Resource res;
@Autowired
private CountWords countWords;
@Override
public void run(String... args) throws Exception {
Map<String, Integer> words = countWords.getWordsCount(res);
for (String key : words.keySet()) {
System.out.println(key + ": " + words.get(key));
}
}
}
使用CommandLineRunner
,Spring Boot 应用在终端上运行。
@Value("classpath:thermopylae.txt")
private Resource res;
使用@Value
注解,将文件设置为资源。
@Autowired
private CountWords countWords;
我们注入了CountWords
bean。
Map<String, Integer> words = countWords.getWordsCount(res);
for (String key : words.keySet()) {
System.out.println(key + ": " + words.get(key));
}
我们调用getWordsCount()
方法,并接收单词及其频率的映射。 我们遍历映射,并将键/值对打印到控制台。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
使用ResourceLoader
以前,我们使用@Value
注解来加载资源。 以下是ResourceLoader
的替代解决方案。
com/zetcode/MyRunner.java
package com.zetcode;
import com.zetcode.count.CountWords;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
@Autowired
private ResourceLoader resourceLoader;
@Autowired
private CountWords countWords;
@Override
public void run(String... args) throws Exception {
Resource res = resourceLoader.getResource("classpath:thermopylae.txt");
Map<String, Integer> words = countWords.getWordsCount(res);
for (String key : words.keySet()) {
System.out.println(key + ": " + words.get(key));
}
}
}
或者,我们可以使用ResourceLoader
加载资源。
@Autowired
private ResourceLoader resourceLoader;
ResourceLoader
被注入到现场。
Resource res = resourceLoader.getResource("classpath:thermopylae.txt");
Resource
是通过getResource()
方法从资源加载器获得的。
运行应用
该应用在命令行上运行。
$ mvn spring-boot:run -q
...
been: 1
Athenian: 1
alliance: 1
navy: 1
fought: 1
led: 1
delayed: 1
had: 2
during: 1
three: 1
second: 1
Greece: 3
...
使用mvn spring-boot:run
命令,运行应用。 -q
选项禁止 Maven 日志。
在本教程中,我们使用了 Spring Boot 应用中的资源。 我们使用@Value
和ResourceLoader
加载资源文件。
Spring Boot H2 REST 教程
在本教程中,我们将使用 H2 数据库创建一个简单的 Spring Boot RESTful 应用。
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是一种以最少的精力创建独立的,基于生产级别的基于 Spring 的应用的方法。
Apache Tomcat 是由 Apache 软件基金会(ASF)开发的开源 Java Servlet 容器。 Tomcat 实现了几种 Java EE 规范,包括 Java Servlet,JavaServer Pages(JSP),Java EL 和 WebSocket。 Tomcat 可以独立和嵌入式模式运行。
H2 是完全用 Java 创建的开源关系数据库管理系统。 它可以嵌入 Java 应用中或以客户端-服务器模式运行。 它易于部署和安装,占地面积小。
JdbcTemplate
是一个 Spring 库,可以帮助程序员创建与关系数据库和 JDBC 一起使用的应用。 它会处理许多繁琐且容易出错的底层细节,例如处理事务,清理资源以及正确处理异常。 JdbcTemplate
在 Spring 的spring-jdbc
模块中提供。
JSON(JavaScript 对象表示法)是一种轻量级的数据交换格式。 人类可以轻松地进行读写,并通过机器解析并生成 JSON。 JSON 的官方互联网媒体类型为application/json
。 JSON 文件扩展名是.json
。
RESTFul 应用遵循 REST 架构样式,该样式用于设计网络应用。 RESTful 应用生成 HTTP 请求,这些请求对资源执行 CRUD(创建/读取/更新/删除)操作。
应用
我们的应用是一个运行在嵌入式 Tomcat 服务器上的 Spring Boot RESTful 应用。 它以 JSON 格式从 H2 数据库返回数据。 该应用使用JdbcTemplate
简化 JDBC 编程。
$ tree
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ ├── bean
│ │ │ └── City.java
│ │ ├── controller
│ │ │ └── MyController.java
│ │ └── service
│ │ ├── CityService.java
│ │ └── ICityService.java
│ └── resources
│ ├── application.yml
│ ├── data-h2.sql
│ └── schema-h2.sql
└── test
└── java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>SpringBootRestH2</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
</project>
这是 Maven 构建文件。 h2
依赖项是 H2 数据库的驱动程序。 spring-boot-devtools
启用热插拔,禁用模板缓存并启用实时重载。 spring-boot-starter-web
是使用 Spring MVC 构建 Web(包括 RESTful)应用的入门程序。 spring-boot-starter-jdbc
是在 Spring Boot 中使用 JDBC 的入门工具。
application.yml
server:
port: 8086
context-path: /rest
spring:
main:
banner-mode: "off"
datasource:
platform: h2
driverClassName: org.h2.Driver
url: jdbc:h2:mem:test;MODE=PostgreSQL
logging:
level:
org:
springframework: ERROR
application.yml
文件包含 Spring Boot 应用的各种配置设置。 我们具有服务器端口和上下文路径(应用名称)的映射。 使用banner-mode
属性,我们可以关闭 Spring 横幅。 该平台值用在 SQL 初始化脚本中:schema-${platform}.sql
和data-${platform}.sql
。 我们将 H2 设置为使用 PostgreSQL 兼容模式。 H2 数据库在内存中运行。 另外,我们将 spring 框架的日志记录级别设置为ERROR
。 该文件位于src/main/resources
目录中。
City.java
package com.zetcode.bean;
public class City {
private Long id;
private String name;
private int population;
public City() {
}
public City(Long id, String name, int population) {
this.id = id;
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public String toString() {
return "City{" + "id=" + id + ", name=" + name +
", population=" + population + '}';
}
}
这是City
bean。
schema-h2.sql
CREATE TABLE CITIES(ID BIGINT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30), POPULATION BIGINT);
该 SQL 脚本创建CITIES
表。
data-h2.sql
INSERT INTO CITIES(NAME, POPULATION) VALUES('Bratislava', 432000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Budapest', 1759000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Prague', 1280000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Warsaw', 1748000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Los Angeles', 3971000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('New York', 8550000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Edinburgh', 464000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Berlin', 3671000);
该脚本用数据填充表。 这两个脚本都位于类路径的根目录中。
ICityService.java
package com.zetcode.service;
import com.zetcode.bean.City;
import java.util.List;
public interface ICityService {
public List<City> findAll();
public City findById(Long id);
}
ICityService
提供了获取所有城市并通过其 ID 从数据源获取城市的契约方法。
CityService.java
package com.zetcode.service;
import com.zetcode.bean.City;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class CityService implements ICityService {
@Autowired
private JdbcTemplate jtm;
@Override
public List<City> findAll() {
String sql = "SELECT * FROM CITIES";
List<City> cities = jtm.query(sql, new BeanPropertyRowMapper(City.class));
return cities;
}
@Override
public City findById(Long id) {
String sql = "SELECT * FROM CITIES WHERE ID=?";
City city = (City) jtm.queryForObject(sql, new Object[]{id},
new BeanPropertyRowMapper(City.class));
return city;
}
}
CityService
包含findAll()
和findById()
方法的实现。 我们使用 Spring 的JdbcTemplate
来执行 SQL 代码。
@Autowired
private JdbcTemplate jtm;
注入JdbcTemplate
。
String sql = "SELECT * FROM CITIES";
这是从CITIES
表中选择所有城市的 SQL。
List<City> cities = jtm.query(sql, new BeanPropertyRowMapper(City.class));
BeanPropertyRowMapper
将一行转换为指定映射目标类的新实例。
String sql = "SELECT * FROM CITIES WHERE ID=?";
这是用于从CITIES
表中选择由 ID 标识的特定城市的 SQL。
City city = (City) jtm.queryForObject(sql, new Object[]{id},
new BeanPropertyRowMapper(City.class));
要从CITIES
表中获得一行,我们使用queryForObject()
方法。
MyController.java
package com.zetcode.controller;
import com.zetcode.bean.City;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import com.zetcode.service.ICityService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@Autowired
private ICityService cityService;
@RequestMapping("/cities")
public List<City> findCities() {
return cityService.findAll();
}
@RequestMapping("/cities/{userId}")
public City findCity(@PathVariable Long userId) {
return cityService.findById(userId);
}
}
这是 Spring Boot RESTful 应用的控制器类。 @RestController
注解创建一个 RESTful 控制器。 传统的 MVC 控制器使用ModelAndView
,而 RESTful 控制器仅返回对象,并且对象数据以 JSON 或 XML 格式直接写入 HTTP 响应。
@Autowired
private ICityService cityService;
我们在countryService
字段中插入ICityService
。
@RequestMapping("/cities")
public List<City> findCities() {
return cityService.findAll();
}
@RequestMapping
注解用于将 Web 请求映射到 Spring 控制器方法。 在这里,我们将具有/cities
路径的请求映射到控制器的findCities()
方法。 默认请求是 GET 请求。
我们不需要手动将City
域对象转换为 JSON。 因为 Jackson 2 在类路径中((通过spring-boot-starter-web
包含在内),所以 Spring 会自动选择MappingJackson2HttpMessageConverter
将City
实例转换为 JSON。
Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
$ mvn spring-boot:run
使用mvn spring-boot:run
命令,运行应用。 该应用部署在嵌入式 Tomcat 服务器上。
$ curl localhost:8086/rest/cities
[{"id":1,"name":"Bratislava","population":432000},{"id":2,"name":"Budapest","population":1759000},
{"id":3,"name":"Prague","population":1280000},{"id":4,"name":"Warsaw","population":1748000},
{"id":5,"name":"Los Angeles","population":3971000},{"id":6,"name":"New York","population":8550000},
{"id":7,"name":"Edinburgh","population":464000},{"id":8,"name":"Berlin","population":3671000}]
使用curl
命令,我们可以获得所有城市。
$ curl localhost:8086/rest/cities/1
{"id":1,"name":"Bratislava","population":432000}
在这里,我们得到了一个由其 ID 标识的城市。
在本教程中,我们创建了一个 Spring Boot RESTful 应用,该应用以 JSON 格式从 H2 数据库返回数据。 您可能也对相关教程感兴趣: Spring Boot Thymeleaf 教程, Spring Boot Mustache 教程, Spring Boot Swing 集成教程, Spring 传统的 Spring 应用简介中的 Web 应用, Spring Boot RESTFul 应用,JdbcTemplate
。
Spring Boot RestTemplate
Spring Boot RestTemplate
教程展示了如何在 Spring 应用中使用RestTemplate
创建同步 HTTP 请求。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。
RestTemplate
RestTemplate
是执行 HTTP 请求的同步客户端。 它在基础 HTTP 客户端库(例如 JDK HttpURLConnection
,Apache HttpComponents 等)上使用简单的模板方法 API。
从 Spring 5.0 开始,可以使用新客户端WebClient
创建同步和异步请求。 在将来的版本中,将不推荐使用RestTemplate
,而推荐使用WebClient
。
Spring Boot RestTemplate
示例
在以下应用中,我们创建一个自定义测试服务器,该服务器生成 JSON 数据,并使用RestTemplate
生成 HTTP 请求并使用返回的 JSON 数据。
创建 JSON 服务器
为此,我们使用 Node 创建一个 JSON 测试服务器。
$ node --version
v11.2.0
我们显示 Node 的版本。
$ npm init
$ npm i -g json-server
$ npm i faker fs
我们初始化 Node 项目并安装json-server
,faker
和fs
模块。 json-server
用于创建测试 JSON 服务器,faker
用于生成测试数据,fs
用于 JavaScript 中的文件系统。
generate_fake_users.js
const faker = require('faker')
const fs = require('fs')
function generateUsers() {
let users = []
for (let id=1; id <= 100; id++) {
let firstName = faker.name.firstName()
let lastName = faker.name.lastName()
let email = faker.internet.email()
users.push({
"id": id,
"first_name": firstName,
"last_name": lastName,
"email": email
})
}
return { "users": users }
}
let dataObj = generateUsers();
fs.writeFileSync('data.json', JSON.stringify(dataObj, null, '\t'));
使用仿造者,我们可以生成具有 ID,名字,姓氏和电子邮件属性的一百个用户。 数据被写入data.json
文件。 该文件由json-server
使用。
$ node generate_fake_users.js
我们产生了一百个假用户。
$ json-server --watch data.json
\{^_^}/ hi!
Loading data.json
Done
Resources
http://localhost:3000/users
Home
http://localhost:3000
我们开始json-server
。 现在,我们可以向http://localhost:3000/users
资源创建一个请求,以使用 JSON 获得一百个用户。
Spring Boot 应用
我们创建一个 Spring Boot 应用。 我们需要以下 Maven 依赖项和插件:spring-boot-starter
,spring-web
,jackson-databind
,spring-boot-starter-test
和spring-boot-maven-plugin
。
application.properties
spring.main.banner-mode=off
logging.level.root=INFO
logging.pattern.console=%d{dd-MM-yyyy HH:mm:ss} %magenta([%thread]) %highlight(%-5level) %logger.%M - %msg%n
myrest.url=http://localhost:3000/users
application.properties
是 Spring Boot 中的主要配置文件。 我们关闭 Spring 横幅,将日志记录级别设置为 info,并设置控制台日志记录模式。 我们还设置了一个指向用户资源的 URL 属性。 稍后将使用@Value
检索该属性。
User.java
package com.zetcode.bean;
import com.fasterxml.jackson.annotation.JsonProperty;
public class User {
private int id;
private String firstName;
private String lastName;
private String email;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
@JsonProperty("first_name")
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
@JsonProperty("last_name")
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
final var sb = new StringBuilder("User{");
sb.append("id=").append(id);
sb.append(", firstName='").append(firstName).append('\'');
sb.append(", lastName='").append(lastName).append('\'');
sb.append(", email='").append(email).append('\'');
sb.append('}');
return sb.toString();
}
}
User
bean 映射到 JSON 用户对象。 Spring 使用 Jackson 库将 JSON 数据绑定到 Java 类。 由于 JSON 属性与 Java 属性不匹配,因此我们使用@JsonProperty
来解决此问题。
AppConfig.java
package com.zetcode.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
var factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(3000);
factory.setReadTimeout(3000);
return new RestTemplate(factory);
}
}
我们创建一个配置 bean。 设置RestTemplate
。 SimpleClientHttpRequestFactory
用于设置连接和读取超时。
AppConfig.java
package com.zetcode.config;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import java.time.Duration;
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.setConnectTimeout(Duration.ofMillis(3000))
.setReadTimeout(Duration.ofMillis(3000))
.build();
}
}
或者,我们可以使用RestTemplateBuilder
来完成这项工作。
MyRestService.java
package com.zetcode.service;
import com.zetcode.bean.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class MyRestService {
@Autowired
private RestTemplate myRestTemplate;
@Value("${myrest.url}")
private String restUrl;
public User[] getUsers() {
var users = myRestTemplate.getForObject(restUrl, User[].class);
return users;
}
}
MyRestService
是生成 HTTP 请求的服务类。 它从 JSON 测试服务器获取所有用户。
@Autowired
private RestTemplate myRestTemplate;
我们注入了RestTemplate
bean。
@Value("${myrest.url}")
private String restUrl;
从配置中,我们使用@Value
注解获取 URL。
var users = myRestTemplate.getForObject(restUrl, User[].class);
我们使用getForObject()
方法生成请求。 由于我们期望对象数组,因此我们使用User[].class
语法。
MyRunner.java
package com.zetcode;
import com.zetcode.service.MyRestService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class MyRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);
@Autowired
private MyRestService myRestService;
@Override
public void run(String... args) throws Exception {
var users = myRestService.getUsers();
Arrays.stream(users).limit(10).forEach(todo -> logger.info("{}", todo));
}
}
MyRunner
使用MyRestService
获取用户。 我们向控制台显示前十个用户。
Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
是设置 Spring Boot 应用的入口。
ApplicationTests.java
package com.zetcode;
import com.zetcode.config.AppConfig;
import com.zetcode.service.MyRestService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@RestClientTest(value={MyRestService.class, AppConfig.class})
public class ApplicationTests {
@Autowired
private MyRestService service;
@Test
public void usersNotEmpty() throws Exception {
var users = this.service.getUsers();
assertThat(users).isNotEmpty();
}
@Test
public void hasSizeOneHundred() throws Exception {
var users = this.service.getUsers();
assertThat(users).hasSize(100);
System.out.println(users);
}
}
我们测试getUsers()
服务方法。 我们测试 JSON 数据不为空,并且包含一百个元素。
@RestClientTest(value={MyRestService.class, AppConfig.class})
@RestClientTest
注解用于测试 Spring rest 客户端。 它禁用完全自动配置,并且仅应用与其余客户端测试相关的配置。
$ mvn -q spring-boot:run
...
27-11-2018 15:33:55 [main] INFO com.zetcode.MyRunner.lambda$run$0 - User{id=1, firstName='Ofelia', lastName='Hintz', email='Gustave.Von43@yahoo.com'}
27-11-2018 15:33:55 [main] INFO com.zetcode.MyRunner.lambda$run$0 - User{id=2, firstName='Brian', lastName='Marvin', email='Marina.Shields@hotmail.com'}
27-11-2018 15:33:55 [main] INFO com.zetcode.MyRunner.lambda$run$0 - User{id=3, firstName='Adah', lastName='Marquardt', email='Osbaldo_Halvorson55@hotmail.com'}
27-11-2018 15:33:55 [main] INFO com.zetcode.MyRunner.lambda$run$0 - User{id=4, firstName='Jaycee', lastName='Kulas', email='Claud85@gmail.com'}
...
我们运行该应用。
在本教程中,我们展示了如何在 Spring 应用中使用RestTemplate
创建同步请求。 REST 数据来自 Node 创建的测试 JSON 服务器。 您可能也对相关教程感兴趣: Spring Boot @RestController
教程, Spring Boot H2 REST 教程, Spring Boot RESTFul 应用, Spring Boot REST Data JPA 教程和 Java 教程。
Spring Boot REST XML 教程
Spring Boot REST XML 教程展示了如何在 Spring Boot RESTFul 应用中提供 XML 数据。 我们为 RESTful 控制器创建测试方法。
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架发展的下一步。 它有助于以最小的努力创建独立的,基于生产级的 Spring 应用。 它提倡在 XML 配置上使用约定而不是配置原则。
RESTFul 应用
RESTFul 应用遵循 REST 架构样式,该样式用于设计网络应用。 RESTful 应用生成对资源执行 CRUD(创建/读取/更新/删除)操作的 HTTP 请求。 RESTFul 应用通常以 JSON 或 XML 格式返回数据。
可扩展标记语言(XML)是一种标记语言,它定义了一组规则,用于以人类可读和机器可读的格式对文档进行编码。 XML 通常用于应用之间的数据交换。
Spring Boot REST XML 示例
以下应用是一个 Spring Boot RESTful 应用,它使用 Spring Data JPA 从 H2 数据库返回 XML 格式的数据。
$ tree
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ ├── bean
│ │ │ ├── Cities.java
│ │ │ └── City.java
│ │ ├── controller
│ │ │ └── MyController.java
│ │ ├── repository
│ │ │ └── CityRepository.java
│ │ └── service
│ │ ├── CityService.java
│ │ └── ICityService.java
│ └── resources
│ ├── application.yml
│ └── import.sql
└── test
└── java
└── com
└── zetcode
└── test
└── RestControllerTest.java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootrestxml</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 h2
依赖项包括 H2 数据库驱动程序。 jackson-dataformat-xml
添加了 Jackson XML 序列化器和反序列化器。
Spring Boot 启动器是一组方便的依赖项描述符,可以极大地简化 Maven 配置。 spring-boot-starter-parent
具有 Spring Boot 应用的一些常用配置。 spring-boot-starter-web
是使用 Spring MVC(包括 RESTFul 应用)构建 Web 应用的入门工具。 它使用 Tomcat 作为默认的嵌入式容器。 spring-boot-starter-data-jpa
是将 Spring Data JPA 与 Hibernate 结合使用的入门工具。 spring-boot-starter-test
是使用包含 JUnit,Hamcrest 和 Mockito 的库测试 Spring Boot 应用的入门程序。
由于从 Java 11 中的 Java SE 中删除了 JAXB API,因此我们需要添加jaxb-api
依赖项。
在spring-boot-maven-plugin
提供了 Maven 的 Spring Boot 支持,使我们能够打包可执行的 JAR 或 WAR 档案。 它的spring-boot:run
目标运行 Spring Boot 应用。
application.yml
server:
port: 8086
servlet:
context-path: /rest
spring:
main:
banner-mode: "off"
jpa:
database: h2
hibernate:
dialect: org.hibernate.dialect.H2Dialect
ddl-auto: create-drop
logging:
level:
org:
springframework: ERROR
在application.yml
文件中,我们编写了 Spring Boot 应用的各种配置设置。 port
设置服务器端口和context-path
上下文路径(应用名称)。 完成这些设置后,我们可以通过localhost:8086/rest/
访问该应用。 使用banner-mode
属性,我们可以关闭 Spring 横幅。
JPA database
值指定要操作的目标数据库。 在本例中,我们指定了 Hibernate 方言org.hibernate.dialect.H2Dialect
。 ddl-auto
是数据定义语言模式; create-drop
选项将自动创建和删除数据库模式。
H2 数据库在内存中运行。 另外,我们将 spring 框架的日志记录级别设置为ERROR
。 在application.yml
文件位于中src/main/resources
目录。
City.java
package com.zetcode.bean;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import java.io.Serializable;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "CITIES")
@JacksonXmlRootElement(localName = "City")
public class City implements Serializable {
private static final long serialVersionUID = 21L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@JacksonXmlProperty(isAttribute = true)
private Long id;
@JacksonXmlProperty
private String name;
@JacksonXmlProperty
private int population;
public City() {
}
public City(Long id, String name, int population) {
this.id = id;
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public String toString() {
return "City{" + "id=" + id + ", name=" + name
+ ", population=" + population + '}';
}
@Override
public int hashCode() {
int hash = 5;
hash = 37 * hash + Objects.hashCode(this.id);
hash = 37 * hash + Objects.hashCode(this.name);
hash = 37 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
}
这是City
实体。 每个实体必须至少定义两个注解:@Entity
和@Id
。 此前,我们已经设置了ddl-auto
选项,create-drop
这意味着 Hibernate 会创建这个实体表模式。
@Entity
@Table(name = "CITIES")
@JacksonXmlRootElement(localName = "City")
public class City implements Serializable {
@Entity
注解指定该类是一个实体,并映射到数据库表。 @Table
注解指定要用于映射的数据库表的名称。 使用@JacksonXmlRootElement(localName = "City")
注解,我们为 XML 输出根元素设置了名称。
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@JacksonXmlProperty(isAttribute = true)
private Long id;
@Id
注解指定实体的主键,@GeneratedValue
提供生成主键值的策略。 使用@JacksonXmlProperty(isAttribute = true)
,我们将id
设置为 XML 输出中City
元素的属性。
@JacksonXmlProperty
private String name;
@JacksonXmlProperty
private int population;
使用@JacksonXmlProperty
,我们将name
和population
属性设置为 XML 输出中 City 元素的属性。
Cities.java
package com.zetcode.bean;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@JacksonXmlRootElement
public class Cities implements Serializable {
private static final long serialVersionUID = 22L;
@JacksonXmlProperty(localName = "City")
@JacksonXmlElementWrapper(useWrapping = false)
private List<City> cities = new ArrayList<>();
public List<City> getCities() {
return cities;
}
public void setCities(List<City> cities) {
this.cities = cities;
}
}
Cities
bean 是一个帮助 bean,用于获取更好的 XML 输出。
@JacksonXmlProperty(localName = "City")
@JacksonXmlElementWrapper(useWrapping = false)
private List<City> cities = new ArrayList<>();
使用@JacksonXmlProperty
和@JacksonXmlElementWrapper
注解,我们确保Cities
元素中嵌套了ArrayList
城市对象的City
元素。
import.sql
INSERT INTO CITIES(NAME, POPULATION) VALUES('Bratislava', 432000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Budapest', 1759000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Prague', 1280000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Warsaw', 1748000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Los Angeles', 3971000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('New York', 8550000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Edinburgh', 464000);
INSERT INTO CITIES(NAME, POPULATION) VALUES('Berlin', 3671000);
模式是由 Hibernate 自动创建的。 之后,执行import.sql
文件以用数据填充 H2 表。
CityRepository.java
package com.zetcode.repository;
import com.zetcode.bean.City;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CityRepository extends CrudRepository<City, Long> {
}
通过从 Spring CrudRepository
扩展,我们将为我们的数据存储库实现一些方法,包括findAll()
和findById()
。 这样,我们节省了大量样板代码。
ICityService.java
package com.zetcode.service;
import com.zetcode.bean.City;
import com.zetcode.bean.Cities;
import java.util.Optional;
public interface ICityService {
Cities findAll();
Optional<City> findById(Long id);
}
ICityService
提供了获取所有城市并通过其 ID 获得一个城市的契约方法。
CityService.java
package com.zetcode.service;
import com.zetcode.bean.Cities;
import com.zetcode.bean.City;
import com.zetcode.repository.CityRepository;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CityService implements ICityService {
@Autowired
private CityRepository repository;
@Override
public Cities findAll() {
var cities = (List<City>) repository.findAll();
var mycities = new Cities();
mycities.setCities(cities);
return mycities;
}
@Override
public Optional<City> findById(Long id) {
var city = repository.findById(id);
return city;
}
}
CityService
包含findAll()
和findById()
方法的实现。 我们使用存储库来处理数据。
@Autowired
private CityRepository repository;
注入CityRepository
。
@Override
public Cities findAll() {
var cities = (List<City>) repository.findAll();
var mycities = new Cities();
mycities.setCities(cities);
return mycities;
}
注意,findAll()
方法返回Cities
bean。
MyController.java
package com.zetcode.controller;
import com.zetcode.bean.City;
import com.zetcode.bean.Cities;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import com.zetcode.service.ICityService;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@Autowired
private ICityService cityService;
@RequestMapping(value="/cities", produces=MediaType.APPLICATION_XML_VALUE)
public Cities findCities() {
return cityService.findAll();
}
@RequestMapping(value="/cities/{cityId}", produces=MediaType.APPLICATION_XML_VALUE)
public City findCity(@PathVariable Long cityId) {
var city = cityService.findById(cityId).get();
return city;
}
}
这是 Spring Boot RESTful 应用的控制器类。 @RestController
注解创建一个 RESTful 控制器。 传统的 MVC 控制器使用ModelAndView
,而 RESTful 控制器仅返回对象,并且对象数据通常以 JSON 或 XML 格式直接写入 HTTP 响应(通常)。
@Autowired
private ICityService cityService;
我们在countryService
字段中插入ICityService
。
@RequestMapping(value="/cities", produces=MediaType.APPLICATION_XML_VALUE)
public Cities findCities() {
return cityService.findAll();
}
我们将带有/cities
路径的请求映射到控制器的findCities()
方法。 默认请求是 GET 请求。 通过使用MediaType.APPLICATION_XML_VALUE
,Spring 使用了一个生成 XML 数据的消息转换器。
@RequestMapping(value="/cities/{cityId}", produces=MediaType.APPLICATION_XML_VALUE)
public City findCity(@PathVariable Long cityId) {
var city = cityService.findById(cityId).get();
return city;
}
在第二种方法中,我们返回一个特定的城市。 URL 路径包含要检索的城市的 ID; 我们使用@PathVariable
注解将 URL 模板变量绑定到cityId
参数。
RestControllerTest.java
package com.zetcode.test;
import java.util.List;
import com.zetcode.bean.City;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.*;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Before;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class RestControllerTest {
@Autowired
private TestRestTemplate restTemplate;
@Value("http://localhost:${local.server.port}/${server.servlet.context-path}/cities")
private String appPath;
private City c1, c2, c3;
@Before
public void setUp() {
this.c1 = new City(1L, "Bratislava", 432000);
this.c2 = new City(2L, "Budapest", 1759000);
this.c3 = new City(3L, "Prague", 1280000);
}
@Test
public void allCitiesTest() {
var paramType = new ParameterizedTypeReference<List<City>>() { };
var cities = restTemplate.exchange(appPath, HttpMethod.GET, null, paramType);
assertThat(cities.getBody()).hasSize(8);
assertThat(cities.getBody()).contains(this.c1, this.c2, this.c3);
}
@Test
public void oneCity() {
var city = this.restTemplate.getForObject(appPath + "/1/", City.class);
assertThat(city).extracting("name", "population").containsExactly("Bratislava",
432000);
}
}
RestControllerTest
包含两种测试控制器方法的方法。
Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
$ mvn spring-boot:run
使用mvn spring-boot:run
命令,运行应用。 该应用部署在嵌入式 Tomcat 服务器上。
$ curl localhost:8086/rest/cities
<Cities>
<City id="1"><name>Bratislava</name><population>432000</population></City>
<City id="2"><name>Budapest</name><population>1759000</population></City>
<City id="3"><name>Prague</name><population>1280000</population></City>
<City id="4"><name>Warsaw</name><population>1748000</population></City>
<City id="5"><name>Los Angeles</name><population>3971000</population></City>
<City id="6"><name>New York</name><population>8550000</population></City>
<City id="7"><name>Edinburgh</name><population>464000</population></City>
<City id="8"><name>Berlin</name><population>3671000</population></City>
</Cities>
使用curl
命令,我们可以获得所有城市。
$ curl localhost:8086/rest/cities/1
<City id="1"><name>Bratislava</name><population>432000</population></City>
在这里,我们得到了一个由其 ID 标识的城市。
在本教程中,我们已经从 Spring Boot RESTful 应用以 XML 格式将数据返回给客户端。 我们使用 Spring Data JPA 从 H2 数据库检索数据。 您可能也对相关教程感兴趣:
- Spring Boot CSV 教程
- Spring Boot Jersey 教程
- Spring Boot RestTemplate 教程
- Spring Boot H2 REST 教程
- Spring Boot Thymeleaf 教程
- Spring Boot Mustache 教程
- Spring Boot Swing 集成教程
- Spring Web 应用介绍
- Spring Boot RESTFul 应用
Spring Boot Moustache 教程
在 Spring Boot Mustache 教程中,我们将使用 Mustache 模板引擎和 HSQLDB 数据库创建一个简单的 Spring Boot Web 应用。
Spring 是流行的 Java 应用框架。 Spring Boot 致力于创建独立的,基于生产级别的基于 Spring 的应用,而无任何麻烦。
HSQLDB 是完全用 Java 创建的开源关系数据库管理系统。 它提供了一个小型,快速的多线程事务型数据库引擎,具有基于内存和基于磁盘的表,并支持嵌入式和服务器模式。 它包括一个功能强大的命令行 SQL 工具和简单的 GUI 查询工具。
Moustache
Moustache 是一个简单的 Web 模板系统。 它可用于许多编程语言,包括 Java。 由于没有任何显式的控制流语句(例如if
和else
条件语句或for
循环),因此,Mustache 被描述为无逻辑的。 可以使用节标记处理列表和 lambda 来实现循环和条件评估。
Spring Boot Moustache 示例
以下示例是使用 Mustache 模板引擎的 Spring Boot Web 应用。 应用的数据位于 HSQLDB 数据库中。
$ tree
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ ├── bean
│ │ │ └── Car.java
│ │ ├── controller
│ │ │ └── MyController.java
│ │ └── service
│ │ ├── CarService.java
│ │ └── ICarService.java
│ └── resources
│ ├── application.yml
│ ├── data-hsqldb.sql
│ ├── schema-hsqldb.sql
│ ├── static
│ │ └── css
│ │ └── style.css
│ └── templates
│ ├── index.html
│ └── showCars.html
└── test
└── java
这是项目结构。 模板文件的后缀为.html
。 它们默认位于src/main/resources/template
目录中。 当在 Maven POM 文件中找到依赖项时,Spring Boot 会自动配置 Mustache。
pom.xml
<?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.zetcode</groupId>
<artifactId>SpringBootMustache</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mustache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 spring-boot-devtools
启用热插拔,禁用模板缓存并启用实时重新加载。 spring-boot-starter-mustache
是用于使用 Mustache 构建 Spring MVC 应用的入门程序。 hsqldb
是 HSQLDB 的驱动程序。 spring-boot-starter-jdbc
是在 Spring Boot 中使用 JDBC 的入门工具。
Car.java
package com.zetcode.bean;
public class Car {
private Long id;
private String name;
private int price;
public Car() {}
public Car(Long id, String name, int price) {
this.id = id;
this.name = name;
this.price = price;
}
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 int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" + "id=" + id + ", name=" + name + ", price=" + price + '}';
}
}
这是Car
bean 类。 它包含汽车 ID,名称和价格字段。
application.yml
server:
context-path: /myapp
spring:
main:
banner-mode: "off"
datasource:
platform: hsqldb
logging:
level:
org:
springframework: ERROR
application.yml
是主要的 Spring Boot 配置文件。 context-path
定义 Web 应用的名称。 我们通过localhost:8080/myapp/
URL 访问我们的应用。 使用banner-mode
属性,我们可以关闭 Spring 横幅。 该平台值用于 SQL 初始化脚本:schema-${platform}.sql
和data-${platform}.sql
中。 另外,我们将 spring 框架的日志记录级别设置为ERROR
。
注意,我们没有配置数据源。 如果没有配置数据,Spring 会以内存模式自动配置 HSQLDB。 我们希望有一个内存数据库,因此我们让 Spring 进行自动配置。
schema-hsqldb.sql
CREATE TABLE CARS(ID BIGINT IDENTITY PRIMARY KEY,
NAME VARCHAR(30), PRICE INT);
该 SQL 脚本创建CARS
表。 HSQLDB 使用IDENTITY
子句创建自动增加的列。 默认情况下,id 从零开始。
data-hsqldb.sql
INSERT INTO CARS(NAME, PRICE) VALUES('Audi', 52642);
INSERT INTO CARS(NAME, PRICE) VALUES('Mercedes', 57127);
INSERT INTO CARS(NAME, PRICE) VALUES('Skoda', 9000);
INSERT INTO CARS(NAME, PRICE) VALUES('Volvo', 29000);
INSERT INTO CARS(NAME, PRICE) VALUES('Bentley', 350000);
INSERT INTO CARS(NAME, PRICE) VALUES('Citroen', 21000);
INSERT INTO CARS(NAME, PRICE) VALUES('Hummer', 41400);
INSERT INTO CARS(NAME, PRICE) VALUES('Volkswagen', 21600);
该脚本用数据填充表。 这两个脚本都位于类路径的根目录中。
ICarService.java
package com.zetcode.service;
import com.zetcode.bean.Car;
import java.util.List;
public interface ICarService {
public List<Car> findAll();
}
ICarService
提供了一种从数据源获取所有城市的契约方法。
CarService.java
package com.zetcode.service;
import com.zetcode.bean.Car;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class CarService implements ICarService {
@Autowired
private JdbcTemplate jtm;
@Override
public List<Car> findAll() {
String sql = "SELECT * FROM CARS";
List<Car> cars = jtm.query(sql, new BeanPropertyRowMapper(Car.class));
return cars;
}
}
CarService
包含findAll()
方法的实现。 我们借助JdbcTemplate
从CARS
表中检索所有汽车。
@Autowired
private JdbcTemplate jtm;
注入JdbcTemplate
。
String sql = "SELECT * FROM CARS";
这是要执行的 SQL。 我们从CARS
表中选择所有汽车。
List<Car> cars = jtm.query(sql, new BeanPropertyRowMapper(Car.class));
BeanPropertyRowMapper
将一行转换为指定映射目标类的新实例。
MyController.java
package com.zetcode.controller;
import com.zetcode.bean.Car;
import com.zetcode.service.ICarService;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class MyController {
@Autowired
private ICarService carService;
@RequestMapping("/")
public String index(Model model) {
return "index";
}
@RequestMapping("/showCars")
public ModelAndView showCars() {
List<Car> cars = carService.findAll();
Map<String, Object> params = new HashMap<>();
params.put("cars", cars);
return new ModelAndView("showCars", params);
}
}
这是 Spring Boot Web 应用的控制器类。 控制器被饰以@Controller
注解。 控制器具有两个映射:一个用于主页的映射,一个用于列出所有汽车的映射。 当 Spring Boot 在 Maven POM 文件中检测到 Mustache 启动程序时,它会自动配置 Mustache 视图。
@Autowired
private ICarService carService;
我们将ICarService
注入到带有@Autowired
注解的字段中。
@RequestMapping("/")
public String index(Model model) {
return "index";
}
"index"
是位于预定义template
目录中的视图的名称。
@RequestMapping("/showCars")
public ModelAndView showCars() {
List<Car> cars = carService.findAll();
Map<String, Object> params = new HashMap<>();
params.put("cars", cars);
return new ModelAndView("showCars", params);
}
该控制器方法可提供汽车清单。 我们从汽车服务中找到所有汽车对象,并将结果列表放入参数中。 Spring 将找到名为showCars
的 Mustache 视图,并让引擎将模板与数据结合在一起。
style.css
h2 {color: blue}
td:nth-child(3) {
text-align: right;
}
style.css
是位于src/main/resources/static/css
目录中的静态文件。 它将h2
标签设置为蓝色,并右对齐第三列的数据。
index.html
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<a href="showCars">Show cars</a>
</body>
</html>
index.html
模板文件是应用的主页。 它包含一个检索所有汽车对象的链接。
showCars.html
<!DOCTYPE html>
<html>
<head>
<title>Cars</title>
<link rel="stylesheet" href="css/style.css" />
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<h2>List of cars</h2>
<table>
<tr>
<th>Id</th>
<th>Name</th>
<th>Price</th>
</tr>
{{#cars}}
<tr>
<td>{{id}}</td>
<td>{{name}}</td>
<td>{{price}}</td>
</tr>
{{/cars}}
</table>
</body>
</html>
showCars.html
是 Moustache 模板文件,其中包含要用来自模型的数据填充的占位符。 小 Moustache 使用{{}}
语法。
<link rel="stylesheet" href="css/style.css" />
我们包括静态 CSS 文件。
{{#cars}}
<tr>
<td>{{id}}</td>
<td>{{name}}</td>
<td>{{price}}</td>
</tr>
{{/cars}}
{{#cars}}
语法称为段。 部分根据当前上下文中键的值,将文本块渲染一次或多次。 一节以{{#name}}
开头,以{{/name}}
结束。 如果该值是非空列表,则该部分将显示多次。 在每种情况下,该节的上下文都将设置为列表中的元素。
Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
我们设置了 Spring Boot 应用。 @SpringBootApplication
注解启用自动配置和组件扫描。
图:列出汽车
该应用部署在内置的 Tomcat 服务器上,该服务器监听端口 8080。
在本教程中,我们使用 Mustache 和 HSQLDB 创建了一个 Spring Boot Web 应用。 您可能也对相关教程感兴趣: Spring Boot Thymeleaf 配置, Spring Boot FreeMarker 教程, Spring Boot Thymeleaf 教程, Spring Boot Swing 集成教程, Spring Web 应用简介,独立的 Spring 应用, FreeMarker 教程, Java 教程,游戏简介或 Strips 简介。
{% endraw %}
Spring Boot WebFlux
教程
Spring Boot WebFlux 教程展示了如何使用 WebFlux 创建一个简单的 Spring Boot 反应式 Web 应用。
WebFlux
WebFlux 是一个 Spring Web 反应式框架。 它已添加到 Spring 5 中。它是完全非阻塞的,支持反应流背压,并在 Netty,Undertow 和 Servlet 3.1+容器等服务器上运行。
Spring WebFlux 是传统 Spring MVC 的替代方法。
Spring WebFlux 在内部使用 Project Reactor 及其发布者实现 Flux 和 Mono。 它支持两种编程模型:a)基于注解的反应组件,b)函数式路由和处理。
反应式编程
反应式编程是一种编程范例,它是函数式的,基于事件的,非阻塞的,异步的,并且以数据流处理为中心。 术语反应式来自以下事实:我们对诸如鼠标单击或 I/O 事件之类的更改做出反应。
当我们处理大量流数据时,响应式应用可以更好地扩展,并且效率更高。 反应性应用是非阻塞的; 他们没有使用资源等待流程完成。
响应式应用实现基于事件的模型,在该模型中将数据推送到使用者。 数据的使用者(订阅者)订阅发布者,后者发布异步数据流。
Spring Reactor
Spring Reactor 是一个反应式库,用于根据反应式流规范在 JVM 上构建非阻塞应用。
Reactor 项目提供两种类型的发布者:Mono
和Flux
。 Flux
是产生 0 到 N 个值的发布者。 返回多个元素的操作使用此类型。 Mono
是产生 0 到 1 值的发布者。 它用于返回单个元素的操作。
Spring Boot WebFlux
示例
在以下应用中,我们创建一个带有响应支持的简单 Spring Boot Web 应用。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ └───controller
│ │ MyController.java
│ └───resources
│ application.properties
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootwebfluxsimple</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven pom.xml
文件。 spring-boot-starter-webflux
是用于使用 Spring Framework 的反应式 Web 支持构建 WebFlux 应用的 Spring Boot 入门程序。
resources/application.properties
spring.main.banner-mode=off
在application.properties
中,我们关闭 Spring Boot 横幅。
com/zetcode/MyController.java
package com.zetcode.controller;
import org.reactivestreams.Publisher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
public class MyController {
@GetMapping("/")
public Publisher<String> home() {
return Mono.just("Home page");
}
}
我们有一个简单的 REST 端点,它返回一条消息。 home()
方法的返回类型是Publisher
。 Mono.just()
发出指定的字符串消息。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
这段代码设置了 Spring Boot 应用。
$ mvn spring-boot:run
我们运行该应用并导航到localhost:8080
。
在本教程中,我们创建了一个简单的 Spring Boot WebFlux 应用。
列出所有 Spring Boot 教程。
Spring Boot Thymeleaf 配置
在 Spring Boot Thymeleaf 配置教程中,我们将展示如何使用 Spring Boot Web 应用配置 Thymeleaf。 当 Spring Boot 在 Maven POM 文件中找到 Thymeleaf 依赖项时,它会自动配置 Thymeleaf 模板引擎。 本教程显示了如何在 Java 配置中手动进行操作。
Spring 是流行的 Java 应用框架。 Spring Boot 致力于以最小的努力创建独立的,基于生产级别的基于 Spring 的应用。
Thymeleaf
Thymeleaf 是适用于 Web 和独立环境的现代服务器端 Java 模板引擎。 它基于自然模板的概念:模板文件可以在浏览器中直接打开,并且仍然可以正确显示为网页。
Spring Boot Thymeleaf 示例
以下示例使用 Java 配置通过 Spring Boot 设置 Thymeleaf。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ └───config
│ │ WebConfig.java
│ └───resources
│ └───mytemplates
│ index.html
└───test
└───java
这是项目结构。 Thymeleaf 模板文件位于自定义src/main/resources/mytemplates
目录中。 默认模板目录为src/main/resources/templates
。
pom.xml
<?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.zetcode</groupId>
<artifactId>thymeleafconfigex</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 spring-boot-devtools
启用热插拔,禁用模板缓存并启用实时重新加载。 spring-boot-starter-thymeleaf
是使用 Thymeleaf 构建 Spring MVC 应用的入门工具。 spring-boot-starter-web
是 Web 应用的启动器。
com/zetcode/config/WebConfig.java
package com.zetcode.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
@Description("Thymeleaf template resolver serving HTML 5")
public ClassLoaderTemplateResolver templateResolver() {
var templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("mytemplates/");
templateResolver.setCacheable(false);
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
@Bean
@Description("Thymeleaf template engine with Spring integration")
public SpringTemplateEngine templateEngine() {
var templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
}
@Bean
@Description("Thymeleaf view resolver")
public ViewResolver viewResolver() {
var viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setCharacterEncoding("UTF-8");
return viewResolver;
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
}
在WebConfig
中,我们配置 Thymeleaf 并设置主页的视图和控制器。 模板引擎是用 Java 代码配置的。
@Bean
@Description("Thymeleaf template resolver serving HTML 5")
public ClassLoaderTemplateResolver templateResolver() {
这个 bean 定义了一个模板解析器。 模板解析器将模板解析为TemplateResolution
对象,其中包含其他信息,例如模板模式,缓存,模板的前缀和后缀。 ClassLoaderTemplateResolver
用于加载位于类路径上的模板。
templateResolver.setPrefix("mytemplates/");
我们将模板目录设置为mytemplates
。 使用ClassLoaderTemplateResolver
时,前缀中没有classpath:
。
templateResolver.setTemplateMode("HTML5");
模板引擎将提供 HTML5 内容。
@Bean
@Description("Thymeleaf template engine with Spring integration")
public SpringTemplateEngine templateEngine() {
var templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
}
创建具有 Spring 集成的 Thymeleaf 模板引擎。
@Bean
@Description("Thymeleaf view resolver")
public ViewResolver viewResolver() {
var viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setCharacterEncoding("UTF-8");
return viewResolver;
}
在这里,我们配置一个创建ThymeleafViewResolver
的 bean。 视图解析器负责获取特定操作和语言环境的 View 对象。 然后将视图对象渲染为 HTML 文件。
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
在这个简单的应用中,我们没有特定的控制器类。 我们用addViewController()
方法定义一个自动控制器。
resources/templates/index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Home page</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<p>
<span th:text="'Today is: ' + ${#dates.format(#dates.createNow(), 'dd MMM yyyy HH:mm')}" th:remove="tag"></span>
</p>
</body>
</html>
这是 Thymeleaf 模板文件。 它显示当前日期。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
这段代码设置了 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
$ mvn spring-boot:run
我们启动该应用。
$ curl localhost:8080
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<p>
Today is: 17 Jan 2019 23:46
</p>
</body>
在本教程中,我们使用 Thymeleaf 创建了 Spring Boot Web 应用。 您可能也对相关教程感兴趣: Spring Boot Thymeleaf 教程, Spring Boot 自动控制器, Spring Boot FreeMarker 教程, Spring Boot Swing 集成教程, Spring Web 应用简介,独立的 Spring 应用, FreeMarker 教程或 Java 教程。
Spring Boot 自动控制器
Spring Boot 自动化控制器展示了如何使用ViewControllerRegistry
在 Spring Boot 应用中创建简单的自动化控制器。 我们的应用显示一个显示当前日期的简单页面。 我们使用 FreeMarker 作为模板引擎。
Spring 是流行的 Java 应用框架。 Spring Boot 致力于创建独立的,基于生产级别的基于 Spring 的应用,而无任何麻烦。
FreeMarker 是适用于 Web 和独立环境的服务器端 Java 模板引擎。 模板使用 FreeMarker 模板语言(FTL)编写,这是一种简单的专用语言。
ViewControllerRegistry
有时我们不需要复杂的控制器逻辑,而只想返回一个视图。 ViewControllerRegistry
注册预先配置了状态代码和/或视图的简单自动化控制器。 它的addViewController()
方法将视图控制器映射到给定的 URL 路径(或模式),以便使用预先配置的状态代码和视图来呈现响应。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ ├───config
│ │ │ MvcConfig.java
│ │ └───controller
│ │ MyController.java
│ └───resources
│ └───templates
│ index.ftl
└───test
└───java
└───com
└───zetcode
HomePageTest.java
这是项目结构。 FreeMarker 模板文件的后缀为.ftl
; 它们默认位于resources/templates
目录中。 当 Spring Boot 在 Maven POM 文件中找到依赖关系时,它将自动配置 FreeMarker。 我们还包括一个测试文件。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootautomatedcontroller</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring-boot-starter-freemarker
是用于使用 FreeMarker 构建 Spring MVC 应用的启动器。 spring-boot-starter-test
导入必要的测试模块。 该应用打包到一个 JAR 文件中。
com/zetcode/MvcConfig.java
package com.zetcode.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
}
在MvcConfig
类中,我们为主页配置一个视图和一个控制器。 由于我们在 Maven POM 文件中包含 FreeMarker,因此 Spring Boot 会自动将 FreeMarker 配置为模板引擎。 因此,index
视图被映射到src/main/resources/templates
目录中的index.ftl
模板文件。
resources/templates/index.ftl
<#assign now = .now>
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<p>Today is: ${now?string.short}</p>
</body>
</html>
index.ftl
模板文件是应用的主页。 它显示当前日期。
<#assign now = .now>
在这里,我们将当前日期时间值分配给now
变量。
<p>Today is: ${now?string.short}</p>
我们以短格式打印日期。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
我们设置了 Spring Boot 应用。 @SpringBootApplication
注解启用自动配置和组件扫描。
com/zetcode/HomePageTest.java
package com.zetcode;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
@RunWith(SpringRunner.class)
@SpringBootTest
public class HomePageTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void testHomePage() throws Exception {
this.mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(view().name("index"))
.andDo(print());
}
}
这是对主页的测试。
$ mvn spring-boot:run
我们启动该应用。
$ curl localhost:8080
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<p>Today is: 5/21/19, 1:53 PM</p>
</body>
</html>
使用curl
工具,我们可以检索主页。
在本教程中,我们创建了一个简单的控制器,并在 Spring Boot 中进行了查看,而没有创建特定的控制器类。 我们已经使用 FreeMarker 作为模板引擎。 您可能也对相关教程感兴趣: Spring Boot FreeMarker 教程, Spring Web 应用简介, FreeMarker 教程和 Java 教程或列出所有 Spring Boot 教程。
Spring Boot FreeMarker 教程
在 Spring Boot FreeMarker 教程中,我们将使用 FreeMarker 模板引擎和 H2 数据库创建一个简单的 Spring Boot Web 应用。
Spring 是流行的 Java 应用框架。 Spring Boot 致力于创建独立的,基于生产级别的基于 Spring 的应用,而无任何麻烦。
H2 是完全用 Java 创建的开源关系数据库管理系统。 它可以嵌入 Java 应用中或以客户端-服务器模式运行。 它易于部署和安装,占地面积小。
FreeMarker
FreeMarker 是适用于 Web 和独立环境的服务器端 Java 模板引擎。 模板使用 FreeMarker 模板语言(FTL)编写,这是一种简单的专用语言。
Spring Boot FreeMarker 示例
以下示例使用 FreeMarker 模板引擎。
pom.xml
src
└── main
├── java
│ └── com
│ └── zetcode
│ ├── Application.java
│ ├── model
│ │ └── City.java
│ ├── controller
│ │ └── MyController.java
│ └── service
│ ├── CityService.java
│ └── ICityService.java
└── resources
├── application.yml
├── data-h2.sql
├── schema-h2.sql
├── static
│ └── css
│ └── style.css
└── templates
├── index.ftl
└── showCities.ftl
这是项目结构。 FreeMarker 模板文件的后缀为.ftl
; 它们默认位于src/main/resources/template
目录中。 当 Spring Boot 在 Maven POM 文件中找到依赖关系时,它将自动配置 FreeMarker。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootfreemarkerex</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 h2
包添加了 H2 数据库。 spring-boot-devtools
启用热插拔,禁用模板缓存并启用实时重载。 spring-boot-starter-web
用于使用 Spring MVC 构建包括 RESTful 在内的 Web 应用。 它使用 Tomcat 作为默认的嵌入式容器。 spring-boot-starter-freemarker
是用于使用 FreeMarker 构建 Spring MVC 应用的入门程序。 spring-boot-starter-jdbc
是在 Spring Boot 中使用 JDBC 的入门工具。
com/zetcode/model/City.java
package com.zetcode.model;
import java.util.Objects;
public class City {
private Long id;
private String name;
private int population;
public City() {
}
public City(Long id, String name, int population) {
this.id = id;
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 29 * hash + Objects.hashCode(this.id);
hash = 29 * hash + Objects.hashCode(this.name);
hash = 29 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
if (!Objects.equals(this.id, other.id)) {
return false;
}
return true;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("City{");
sb.append("id=").append(id);
sb.append(", name='").append(name).append('\'');
sb.append(", population=").append(population);
sb.append('}');
return sb.toString();
}
}
这是City
bean 类。 它包含商品 ID,名称和数量。
resources/application.yml
server:
servlet:
context-path: /cities
spring:
main:
banner-mode: "off"
datasource:
platform: h2
logging:
level:
org:
springframework: ERROR
application.yml
是主要的 Spring Boot 配置文件。 context-path
定义 Web 应用的名称。 使用banner-mode
属性,我们可以关闭 Spring 横幅。 该平台值用在 SQL 初始化脚本中:schema-${platform}.sql
和data-${platform}.sql
。 另外,我们将 spring 框架的日志记录级别设置为ERROR
。
注意,我们没有配置数据源。 如果没有配置数据,Spring 会以内存模式自动配置 H2。 我们希望有一个内存数据库,因此我们让 Spring 进行自动配置。
resources/schema-h2.sql
CREATE TABLE cities(id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255), population INT);
该 SQL 脚本创建cities
表。
resources/data-h2.sql
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
该脚本用数据填充表。 这两个脚本都位于类路径的根目录中。
com/zetcode/model/ICityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import java.util.List;
public interface ICityService {
List<City> findAll();
}
ICityService
提供了一种从数据源获取所有城市的契约方法。
com/zetcode/service/CityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class CityService implements ICityService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public List<City> findAll() {
var sql = "SELECT * FROM cities";
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(City.class));
}
}
CityService
包含findAll()
方法的实现。 我们借助JdbcTemplate
从cities
表中检索所有城市。
@Autowired
private JdbcTemplate jdbcTemplate;
注入JdbcTemplate
。
var sql = "SELECT * FROM CITIES";
这是要执行的 SQL。 我们从CITIES
表中选择所有城市。
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(City.class));
BeanPropertyRowMapper
将一行转换为指定映射目标类的新实例。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import com.zetcode.service.ICityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
@Controller
public class MyController {
@Autowired
private ICityService cityService;
@GetMapping("/")
public String index(Model model) {
return "index";
}
@GetMapping("/showCities")
public ModelAndView showCities() {
var cities = cityService.findAll();
var params = new HashMap<String, Object>();
params.put("cities", cities);
return new ModelAndView("showCities", params);
}
}
这是 Spring Boot Web 应用的控制器类。 控制器以@Controller
注解修饰。 控制器具有两个映射:一个用于主页的映射,一个用于列出所有城市的映射。 当 Spring Boot 在 Maven POM 文件中检测到 FreeMarker 启动程序时,它将自动配置 FreeMarker 视图。
@Autowired
private ICityService cityService;
我们将ICityService
注入到带有@Autowired
注解的字段中。
@GetMapping("/")
public String index(Model model) {
return "index";
}
index
是位于预定义template
目录中的视图的名称。
@GetMapping("/showCities")
public ModelAndView showCities() {
var cities = cityService.findAll();
var params = new HashMap<String, Object>();
params.put("cities", cities);
return new ModelAndView("showCities", params);
}
此控制器方法可提供城市列表。 我们从城市服务中找到所有城市对象,并将结果列表放入参数中。 Spring 将找到名为showCities
的 FreeMarker 视图,并让引擎将模板与模型数据连接在一起。
resources/static/css/style.css
h2 {color: blue}
td:nth-child(3) {
text-align: right;
}
style.css
是位于src/main/resources/static/css
目录中的静态文件。 它将 H2 标签设置为蓝色,并右对齐第三列的数据。
resources/templates/index.ftl
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<a href="showCities">Show cities</a>
</body>
</html>
index.ftl
模板文件是应用的主页。 它包含一个检索所有城市的链接。
resources/templates/showCities.ftl
<!DOCTYPE html>
<html>
<head>
<title>Cities</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<h2>List of cities</h2>
<table>
<tr>
<th>Id</th>
<th>Name</th>
<th>Population</th>
</tr>
<#list cities as city>
<tr>
<td>${city.id}</td>
<td>${city.name}</td>
<td>${city.population}</td>
</tr>
</#list>
</table>
</body>
</html>
showCities.ftl
是一个 FreeMarker 模板文件,其中包含要用来自模型的数据填充的占位符。 要访问数据,我们使用${}
变量表达式。
<link rel="stylesheet" href="css/style.css">
我们包括静态 CSS 文件。
<#list cities as city>
<tr>
<td>${city.id}</td>
<td>${city.name}</td>
<td>${city.population}</td>
</tr>
</#list>
我们遍历城市列表,并将每个城市的详细信息放入一个表格行中。 #list
指令用于列出数据集合。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
我们设置了 Spring Boot 应用。 @SpringBootApplication
注解启用自动配置和组件扫描。
图:列出城市
该应用部署在内置的 Tomcat 服务器上,该服务器监听端口 8080。
在本教程中,我们使用 FreeMarker 和 H2 创建了一个 Spring Boot Web 应用。 您可能也对相关教程感兴趣: FreeMarker 教程, Java 教程或列出所有 Spring Boot 教程。
Spring Boot Environment
Spring Boot Environment
显示了如何在 Spring Boot 中读取环境变量。 Spring Boot 应用可以部署在各种环境中,在这种情况下读取环境变量可能会有所帮助。
Spring 是一种流行的 Java 应用框架,而 Spring Boot 是 Spring 的下一步发展,它可以帮助您以最少的精力创建独立的,生产级的基于 Spring 的应用。
Environment
是代表当前应用运行环境的接口。 它可用于获取应用环境的配置文件和属性。
$ echo %JAVA_HOME%
C:\Users\Jano\AppData\Local\Programs\Java\openjdk-11\
在此示例中,我们定义了JAVA_HOME
环境变量。
pom.xml
src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ └── Application.java
│ └── resources
│ ├── application.properties
│ └── logback.xml
└── test
└── java
这是 Spring Boot 应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootenvironment</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 spring-boot-starter
是包括自动配置支持,日志记录和 YAML 在内的核心启动器。 该应用打包到一个 JAR 文件中。
resources/application.properties
spring.main.banner-mode=off
spring.output.ansi.enabled=ALWAYS
logging.pattern.console=%clr(%d{yy-MM-dd E HH:mm:ss.SSS}){blue} %clr(%-5p) %clr(%logger{0}){blue} %clr(%m){faint}%n
app.name=MyApp
application.properties
文件包含应用配置设置。 Spring 具有一些内置的应用属性,我们可以创建自定义属性。 spring.main.banner-mode
属性是 Spring 内置属性; 我们关闭了 Spring 的标志。 接下来的两行设置了带有颜色支持的日志记录。 app.name
是我们的自定义属性,其中包含应用名称。
resources/logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<logger name="org.springframework" level="ERROR"/>
<logger name="com.zetcode" level="INFO"/>
</configuration>
在logback.xml
文件中配置了应用日志记录。 我们设置日志记录级别的级别。 我们不希望我们的输出被不必要的消息所困扰。 spring-boot-starter
依赖项启用登录日志记录。
com/zetcode/Application.java
package com.zetcode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
@SpringBootApplication
public class Application implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
@Autowired
private Environment env;
@Override
public void run(String... args) throws Exception {
logger.info("{}", env.getProperty("JAVA_HOME"));
logger.info("{}", env.getProperty("app.name"));
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
在Application
中,我们创建一个 bean,调用其方法并设置 Spring Boot 应用。 CommandLineRunner
接口指示当SpringApplication
中包含 bean 时应运行它。 它可以用来在 Spring Boot 中创建命令行应用。
@SpringBootApplication
public class Application implements CommandLineRunner {
@SpringBootApplication
注解启用自动配置和组件扫描。
@Autowired
private Environment env;
我们注入Environment
以获得属性。
logger.info("{}", env.getProperty("JAVA_HOME"));
在这里,我们检索JAVA_HOME
环境变量。
logger.info("{}", env.getProperty("app.name"));
也可以使用Environment
从application.properties
文件中获取属性:获取app.name
属性。
$ mvn -q spring-boot:run
...
19-05-23 Thu 18:03:51.558 INFO Application C:\Users\Jano\AppData\Local\Programs\Java\openjdk-11\
19-05-23 Thu 18:03:51.560 INFO Application MyApp
我们运行该应用。 -q
Maven 选项关闭 Maven 消息。
在本教程中,我们使用 Spring Environment
来读取环境变量。 您可能也对相关教程感兴趣: SpringApplicationBuilder
教程,在 Spring Boot 中提供静态内容, Spring Boot DataSourceBuilder
教程, Java 教程或列出所有 Spring Boot 教程。
Spring Boot Swing 集成教程
在 Spring Boot Swing 集成教程中,我们将结合 Spring Boot 框架和 Swing 库。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。
Swing 是 Java 编程语言的主要 GUI 工具包。 Swing 完全用 Java 编写。
pom.xml
src
├── main
│ └── java
│ └── com
│ └── zetcode
│ └── gui
│ └── SwingApp.java
└── test
└── java
这是 Spring Boot 应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootswing</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 spring-boot-starter
是包括自动配置支持,日志记录和 YAML 在内的核心启动器。 该应用打包到一个 JAR 文件中。
com/zetcode/gui/SwingApp.java
package com.zetcode.gui;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
@SpringBootApplication
public class SwingApp extends JFrame {
public SwingApp() {
initUI();
}
private void initUI() {
var quitButton = new JButton("Quit");
quitButton.addActionListener((ActionEvent event) -> {
System.exit(0);
});
createLayout(quitButton);
setTitle("Quit button");
setSize(300, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private void createLayout(JComponent... arg) {
var pane = getContentPane();
var gl = new GroupLayout(pane);
pane.setLayout(gl);
gl.setAutoCreateContainerGaps(true);
gl.setHorizontalGroup(gl.createSequentialGroup()
.addComponent(arg[0])
);
gl.setVerticalGroup(gl.createSequentialGroup()
.addComponent(arg[0])
);
}
public static void main(String[] args) {
var ctx = new SpringApplicationBuilder(SwingApp.class)
.headless(false).run(args);
EventQueue.invokeLater(() -> {
var ex = ctx.getBean(SwingApp.class);
ex.setVisible(true);
});
}
}
这个简单的 Swing 应用的面板上有一个JButton
。 单击该按钮可终止应用。
@SpringBootApplication
public class SwingApp extends JFrame {
Swing 应用以@SpringBootApplication
注解修饰。 注解启用 Spring Boot 服务。
var quitButton = new JButton("Quit");
在这里,我们创建一个按钮组件。 此构造器将字符串标签作为参数。
quitButton.addActionListener((ActionEvent event) -> {
System.exit(0);
});
我们将一个动作监听器插入按钮。 当我们单击按钮时,将调用监听器的actionPerformed()
方法。 该操作通过调用System.exit()
方法来终止应用。
createLayout(quitButton);
子组件需要放入容器中。 我们将任务委托给createLayout()
方法。
var gl = new GroupLayout(pane);
pane.setLayout(gl);
我们使用GroupLayout
进行应用布局。
ConfigurableApplicationContext ctx = new SpringApplicationBuilder(SwingApp.class)
.headless(false).run(args);
Spring Boot 应用是使用SpringApplicationBuilder
创建的。 我们关闭无头模式,该模式适用于服务器应用。
EventQueue.invokeLater(() -> {
var ex = ctx.getBean(SwingApp.class);
ex.setVisible(true);
});
我们从应用上下文中检索 Swing 应用 bean。 invokeLater()
方法将应用放置在 Swing 事件队列中。 它用于确保所有 UI 更新都是并发安全的。 换句话说,这是为了防止 GUI 在某些情况下挂起。
$ mvn spring-boot:run -q
我们运行该应用。 -q
Maven 选项关闭 Maven 消息。
在本教程中,我们使用 Spring Boot 框架创建了一个 Swing 应用。 您可能也对相关教程感兴趣: Java Swing 教程,独立的 Spring 应用, Java 教程或列出所有 Spring Boot 教程。
在 Spring Boot 中提供图像文件
在本教程中,我们展示了如何在 Spring Boot RESTful Web 应用中提供图像文件。 该图像是位于资源目录中的 JPEG 文件。
Spring 是用于开发 Java 企业应用的 Java 应用框架。 它还有助于集成各种企业组件。 Spring Boot 使创建具有 Spring 动力的生产级应用和服务变得很容易,而对安装的要求却最低。
我们将展示将图像数据发送到客户端的三种方式。
Spring 图像示例
该 Web 应用在src/main/resources/image
目录中包含一个sid.jpg
文件。 ClassPathResource
用于获取图像文件。
pom.xml
src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ └── controller
│ │ └── MyController.java
│ └── resources
│ └── image
│ └── sid.jpg
└── test
└── java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springimage</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 Spring Boot 启动器是一组有用的依赖项描述符,可大大简化 Maven 配置。 spring-boot-starter-parent
具有 Spring Boot 应用的一些常用配置。 spring-boot-starter-web
是使用 Spring MVC 构建 Web 应用的入门工具。 它使用 Tomcat 作为默认的嵌入式服务器。 spring-boot-maven-plugin
在 Maven 中提供了 Spring Boot 支持,使我们可以打包可执行的 JAR 或 WAR 档案。 它的spring-boot:run
目标执行 Spring Boot 应用。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用组件扫描和自动配置。
使用HttpServletResponse
提供图像
在第一种情况下,我们直接写入HttpServletResponse
。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@RequestMapping(value = "/sid", method = RequestMethod.GET,
produces = MediaType.IMAGE_JPEG_VALUE)
public void getImage(HttpServletResponse response) throws IOException {
var imgFile = new ClassPathResource("image/sid.jpg");
response.setContentType(MediaType.IMAGE_JPEG_VALUE);
StreamUtils.copy(imgFile.getInputStream(), response.getOutputStream());
}
}
在此控制器中,我们获取图像资源并将其直接写入响应对象。
var imgFile = new ClassPathResource("image/sid.jpg");
我们从类路径与ClassPathResource
图片资源中(src/main/resource
是在 Java 类路径)。
response.setContentType(MediaType.IMAGE_JPEG_VALUE);
响应的内容类型设置为MediaType.IMAGE_JPEG_VALUE
。
StreamUtils.copy(imgFile.getInputStream(), response.getOutputStream());
使用StreamUtils
,我们将数据从图像复制到响应对象。
用ResponseEntity
提供图像
在第二种情况下,我们使用ResponseEntity
。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import java.io.IOException;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@RequestMapping(value = "/sid", method = RequestMethod.GET,
produces = MediaType.IMAGE_JPEG_VALUE)
public ResponseEntity<byte[]> getImage() throws IOException {
var imgFile = new ClassPathResource("image/sid.jpg");
byte[] bytes = StreamUtils.copyToByteArray(imgFile.getInputStream());
return ResponseEntity
.ok()
.contentType(MediaType.IMAGE_JPEG)
.body(bytes);
}
}
getImage()
方法的返回类型设置为ResponseEntity<byte[]>
。
byte[] bytes = StreamUtils.copyToByteArray(imgFile.getInputStream());
使用StreamUtils.copyToByteArray()
,我们将图像数据复制到字节数组中。
return ResponseEntity
.ok()
.contentType(MediaType.IMAGE_JPEG)
.body(bytes);
字节数组提供给ResponseEntity
的主体。
使用ResponseEntity
和InputStreamResource
提供图像
在第三种情况下,我们使用ResponseEntity
和InputStreamResource
。 InputStreamResource
是 Spring 对低级资源的抽象。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import java.io.IOException;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@RequestMapping(value = "/sid", method = RequestMethod.GET,
produces = MediaType.IMAGE_JPEG_VALUE)
public ResponseEntity<InputStreamResource> getImage() throws IOException {
var imgFile = new ClassPathResource("image/sid.jpg");
return ResponseEntity
.ok()
.contentType(MediaType.IMAGE_JPEG)
.body(new InputStreamResource(imgFile.getInputStream()));
}
}
getImage()
方法的返回类型设置为ResponseEntity<InputStreamResource>
。
return ResponseEntity
.ok()
.contentType(MediaType.IMAGE_JPEG)
.body(new InputStreamResource(imgFile.getInputStream()));
ResponseEntity
的主体返回InputStreamResource
。
$ mvn spring-boot:run
我们启动 Spring Boot 应用。
我们导航到http://localhost:8080/sid
在浏览器中显示图像。
在本教程中,我们展示了如何从 Spring Boot 应用向客户端发送图像数据。 我们使用了三种不同的方法。 您可能也对这些相关教程感兴趣:用 Java 显示图像, Java 教程或列出所有 Spring Boot 教程。
在 Spring Boot 中创建 PDF 报告
Spring Boot 创建 PDF 报告教程展示了如何在 Spring Boot Web 应用中提供 PDF 文件。 该报告是使用 iText 库生成的。
iText 是一个开放源代码库,用于在 Java 中创建和处理 PDF 文件。
Spring 是用于开发 Java 企业应用的 Java 应用框架。 它还有助于集成各种企业组件。 Spring Boot 使创建具有 Spring 动力的生产级应用和服务变得很容易,而对安装的要求却最低。
H2 是完全用 Java 实现的开源关系数据库管理系统。 它可以嵌入 Java 应用中或以客户端-服务器模式运行。 它占地面积小,易于部署和安装。 它包含一个基于浏览器的控制台应用,用于查看和编辑数据库表。
Spring Data JPA 是总括性 Spring Data 项目的一部分,该项目使实现基于 JPA 的存储库变得更加容易。 Spring Data JPA 使用 JPA 将数据存储在关系数据库中。 它可以在运行时从存储库接口自动创建存储库实现。
Spring Boot 服务 PDF 示例
以下 Spring Boot 应用从数据库表中加载数据,并使用 iText 库从中生成 PDF 报告。 它使用ResponseEntity
和InputStreamResource
将 PDF 数据发送到客户端。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ ├───controller
│ │ │ MyController.java
│ │ ├───model
│ │ │ City.java
│ │ ├───repository
│ │ │ CityRepository.java
│ │ ├───service
│ │ │ CityService.java
│ │ │ ICityService.java
│ │ └───util
│ │ GeneratePdfReport.java
│ └───resources
│ application.yml
│ import.sql
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootservepdf</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>4.2.2</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。
Spring Boot 启动器是一组有用的依赖项描述符,可大大简化 Maven 配置。 spring-boot-starter-parent
具有 Spring Boot 应用的一些常用配置。 spring-boot-starter-web
是使用 Spring MVC 构建 Web 应用的入门工具。 它使用 Tomcat 作为默认的嵌入式容器。 spring-boot-starter-data-jpa
是将 Spring Data JPA 与 Hibernate 结合使用的入门工具。
此外,我们还包括 H2 数据库和 iText 库的依赖项。
spring-boot-maven-plugin
在 Maven 中提供了 Spring Boot 支持,使我们可以打包可执行的 JAR 或 WAR 档案。 它的spring-boot:run
目标运行 Spring Boot 应用。
com/zetcode/model/City.java
package com.zetcode.model;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "cities")
public class City {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int population;
public City() {
}
public City(String name, int population) {
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.id);
hash = 79 * hash + Objects.hashCode(this.name);
hash = 79 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append("City{id=").append(id).append(", name=")
.append(name).append(", population=")
.append(population).append("}");
return builder.toString();
}
}
这是City
实体。 每个实体必须至少定义两个注解:@Entity
和@Id
。 spring.jpa.hibernate.ddl-auto
属性的默认值为create-drop
,这意味着 Hibernate 将根据该实体创建表架构。
@Entity
@Table(name = "cities")
public class City {
@Entity
注解指定该类是一个实体,并映射到数据库表。 @Table
实体指定要用于映射的数据库表的名称。
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Id
注解指定实体的主键,@GeneratedValue
提供规范主键值的生成策略。
resources/application.yml
spring:
main:
banner-mode: "off"
logging:
level:
org:
springframework: ERROR
application.yml
是主要的 Spring Boot 配置文件。 使用banner-mode
属性,我们可以关闭 Spring 横幅。 Spring 框架日志记录设置为ERROR
。
resources/import.sql
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Suzhou', 4327066);
INSERT INTO cities(name, population) VALUES('Zhengzhou', 4122087);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
INSERT INTO cities(name, population) VALUES('Brest', 139163);
INSERT INTO cities(name, population) VALUES('Bucharest', 1836000);
模式是由 Hibernate 自动创建的。 之后,将执行import.sql
文件以将数据填充到表中。
com/zetcode/repository/CityRepository.java
package com.zetcode.repository;
import com.zetcode.model.City;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CityRepository extends CrudRepository<City, Long> {
}
通过从 Spring CrudRepository
扩展,我们为数据库实现了一些方法,包括findAll()
和findOne()
。 这样,我们不必编写很多样板代码。
com/zetcode/service/ICityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import java.util.List;
public interface ICityService {
List<City> findAll();
}
ICityService
提供了一种从数据库中获取所有城市的契约方法。
com/zetcode/service/CityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import com.zetcode.repository.CityRepository;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CityService implements ICityService {
@Autowired
private CityRepository repository;
@Override
public List<City> findAll() {
return (List<City>) repository.findAll();
}
}
CityService
包含findAll()
方法的实现。 我们使用存储库从数据库检索数据。
@Autowired
private CityRepository repository;
注入CityRepository
。
return (List<City>) repository.findAll();
存储库的findAll()
方法返回城市列表。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import com.zetcode.model.City;
import com.zetcode.service.ICityService;
import com.zetcode.util.GeneratePdfReport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.io.ByteArrayInputStream;
import java.util.List;
@Controller
public class MyController {
@Autowired
private ICityService cityService;
@RequestMapping(value = "/pdfreport", method = RequestMethod.GET,
produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<InputStreamResource> citiesReport() {
var cities = (List<City>) cityService.findAll();
ByteArrayInputStream bis = GeneratePdfReport.citiesReport(cities);
var headers = new HttpHeaders();
headers.add("Content-Disposition", "inline; filename=citiesreport.pdf");
return ResponseEntity
.ok()
.headers(headers)
.contentType(MediaType.APPLICATION_PDF)
.body(new InputStreamResource(bis));
}
}
citiesReport()
方法返回生成的 PDF 报告。 Resource
接口抽象了对低级资源的访问; InputStreamResource
是它对流资源的实现。
@Autowired
private ICityService cityService;
我们将ICityService
对象注入到属性中。 服务对象用于从数据库检索数据。
var cities = (List<City>) cityService.findAll();
我们使用findAll()
方法查找所有城市。
ByteArrayInputStream bis = GeneratePdfReport.citiesReport(cities);
GeneratePdfReport.citiesReport()
使用 iText 库从城市列表中生成 PDF 文件。
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "inline; filename=citiesreport.pdf");
通过将Content-Disposition
设置为inline
,PDF 文件将直接显示在浏览器中。
return ResponseEntity
.ok()
.headers(headers)
.contentType(MediaType.APPLICATION_PDF)
.body(new InputStreamResource(bis));
我们使用ResponseEntity
创建响应。 我们指定标题,内容类型和主体。 内容类型为MediaType.APPLICATION_PDF
。 身体是InputStreamResource
。
com/zetcode/util/GeneratePdfReport.java
package com.zetcode.util;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.FontFactory;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.zetcode.model.City;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.List;
public class GeneratePdfReport {
private static final Logger logger = LoggerFactory.getLogger(GeneratePdfReport.class);
public static ByteArrayInputStream citiesReport(List<City> cities) {
Document document = new Document();
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
PdfPTable table = new PdfPTable(3);
table.setWidthPercentage(60);
table.setWidths(new int[]{1, 3, 3});
Font headFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD);
PdfPCell hcell;
hcell = new PdfPCell(new Phrase("Id", headFont));
hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(hcell);
hcell = new PdfPCell(new Phrase("Name", headFont));
hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(hcell);
hcell = new PdfPCell(new Phrase("Population", headFont));
hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(hcell);
for (City city : cities) {
PdfPCell cell;
cell = new PdfPCell(new Phrase(city.getId().toString()));
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(cell);
cell = new PdfPCell(new Phrase(city.getName()));
cell.setPaddingLeft(5);
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.setHorizontalAlignment(Element.ALIGN_LEFT);
table.addCell(cell);
cell = new PdfPCell(new Phrase(String.valueOf(city.getPopulation())));
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
cell.setPaddingRight(5);
table.addCell(cell);
}
PdfWriter.getInstance(document, out);
document.open();
document.add(table);
document.close();
} catch (DocumentException ex) {
logger.error("Error occurred: {0}", ex);
}
return new ByteArrayInputStream(out.toByteArray());
}
}
GeneratePdfReport
根据提供的数据创建 PDF 文件。
ByteArrayOutputStream out = new ByteArrayOutputStream();
数据将被写入ByteArrayOutputStream
。
PdfPTable table = new PdfPTable(3);
我们将数据放在表格中; 为此,我们有PdfPTable
类。 该表具有三列:ID,名称和人口。
Font headFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD);
对于表头,我们使用粗体的 Helvetica 字体。
PdfPCell hcell;
hcell = new PdfPCell(new Phrase("Id", headFont));
hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(hcell);
数据放置在表单元格内,由PdfPCell
表示。 使用setHorizontalAlignment()
方法将文本水平对齐。
PdfWriter.getInstance(document, out);
使用PdfWriter
,将文档写入ByteArrayOutputStream
。
document.open();
document.add(table);
该表将插入到 PDF 文档中。
document.close();
为了将数据写入ByteArrayOutputStream
,必须关闭文档。
return new ByteArrayInputStream(out.toByteArray());
最后,数据返回为ByteArrayInputStream
。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。
$ mvn spring-boot:run
我们启动 Spring Boot 应用。
我们导航到http://localhost:8080/pdfreport
以生成报告。
在本教程中,我们展示了如何将生成的 PDF 文件发送回客户端。 PDF 报告是使用 iText 生成的,数据来自 H2 数据库。 我们使用 Spring Data JPA 访问数据。 您可能也对这些相关教程感兴趣: Spring Boot 基本注解, Spring Boot H2 教程, Spring Boot JasperReports Web 集成, Java 教程或列出所有 Spring Boot 教程。
Spring Boot 基本注解
Spring Boot 基本注解教程展示了如何使用基本的 Spring Boot 注解,包括@Bean
,@Service
,@Configuration
,@Controller
,@RequestMapping
,@Repository
,@Autowired
和@SpringBootApplication
。
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架发展的下一步。 它有助于以最小的努力创建独立的,基于生产级的 Spring 应用。 它不再使用 XML 配置,并实现了约定而非配置原则。
注解是元数据的一种形式,它提供有关程序的数据,该数据不是程序本身的一部分。 注解对它们注解的代码的操作没有直接影响。
Spring Boot 基本注解
在示例应用中,我们具有以下 Spring Boot 注解:
@Bean
- 表示方法产生一个由 Spring 管理的 bean。@Service
- 表示带注解的类是服务类。@Repository
- 指示带注解的类是存储库,它是数据访问和存储的抽象。@Configuration
- 表示一个类是可能包含 bean 定义的配置类。@Controller
- 将类标记为 Web 控制器,能够处理请求。@RequestMapping
- 将 HTTP 请求及其路径映射到控制器方法。@Autowired
- 标记要通过 Spring 依赖注入自动装配的构造器,字段或设置方法。@SpringBootApplication
- 启用 Spring Boot 自动配置和组件扫描。
@Component
是 Spring 托管组件的通用构造型。 在自动扫描时,它将类转换为 Spring bean。 当使用基于注解的配置和类路径扫描时,被此注解修饰的类被视为自动检测的候选对象。 @Repository
,@Service
和@Controller
是@Component
的特殊化,用于更特定的用例。
在示例中,还有 Hibernate @Entity
,@Table
,@Id
和@GeneratedValue
注解。
Spring Boot 基本注解示例
以下应用是一个 Spring Boot 应用,它使用 Spring Data JPA 从 H2 数据库返回数据。 该应用使用 FreeMarker 作为模板引擎。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ ├───controller
│ │ │ MyController.java
│ │ ├───model
│ │ │ City.java
│ │ ├───repository
│ │ │ CityRepository.java
│ │ └───service
│ │ CityService.java
│ │ ICityService.java
│ └───resources
│ │ application.yaml
│ │ import.sql
│ ├───static
│ │ └───css
│ │ style.css
│ └───templates
│ index.ftl
│ showCities.ftl
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootbasicannotations</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 它包含 Freemaker,Spring Data JPA 和 H2 数据库的依赖项。 当 Spring Boot 在pom.xml
中找到 Freemaker 和 H2 时,它将自动配置它们。 我们可以立即使用它们。
resources/application.yml
server:
servlet:
context-path: /myapp
spring:
main:
banner-mode: "off"
datasource:
platform: h2
logging:
level:
org:
springframework: ERROR
在application.yml
文件中,我们编写了 Spring Boot 应用的各种配置设置。
com/zetcode/model/City.java
package com.zetcode.model;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "cities")
public class City {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int population;
public City() {
}
public City(String name, int population) {
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.id);
hash = 79 * hash + Objects.hashCode(this.name);
hash = 79 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append("City{id=").append(id).append(", name=")
.append(name).append(", population=")
.append(population).append("}");
return builder.toString();
}
}
这是City
实体。 每个实体必须至少定义两个注解:@Entity
和@Id
。 @Entity
注解指定该类是一个实体,并映射到数据库表。 @Table
注解指定要用于映射的数据库表的名称。 @Id
注解指定了实体的主键,@GeneratedValue
为主键的值提供了生成策略的规范。
resources/import.sql
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
模式是由 Hibernate 自动创建的。 之后,将执行import.sql
文件以将数据填充到表中。
com/zetcode/repository/CityRepository.java
package com.zetcode.repository;
import com.zetcode.model.City;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CityRepository extends CrudRepository<City, Long> {
}
@Repository
注解用于定义存储库。
com/zetcode/service/ICityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import java.util.List;
public interface ICityService {
List<City> findAll();
}
ICityService
提供了获取所有城市的契约方法。
com/zetcode/service/CityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import com.zetcode.repository.CityRepository;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CityService implements ICityService {
@Autowired
private CityRepository cityRepository;
@Override
public List<City> findAll() {
return (List<City>) cityRepository.findAll();
}
}
@Service
注解将CityService
声明为服务类; 提供业务服务的类。 @Autowired
注解标记要插入CityRepository
的cityRepository
字段。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import com.zetcode.service.ICityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
import java.util.Map;
@Controller
public class MyController {
@Autowired
private ICityService cityService;
@RequestMapping("/")
public String index(Model model) {
return "index";
}
@RequestMapping("/cities")
public ModelAndView showCities() {
var cities = cityService.findAll();
Map<String, Object> params = new HashMap<>();
params.put("cities", cities);
return new ModelAndView("showCities", params);
}
}
@Controller
注解将一个类标记为 Web 控制器。 @RequestMapping
将 HTTP 请求及其路径映射到控制器方法。 在第二种情况下,它将/cities
URL 映射到showCities()
方法。
resources/templates/index.ftl
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<a href="cities">Show cities</a>
</body>
</html>
这是index.ftl
模板文件。 它包含用于创建显示所有城市的请求的链接。
resources/templates/showCities.ftl
<!DOCTYPE html>
<html>
<head>
<title>Cities</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<h2>List of cities</h2>
<table>
<tr>
<th>Id</th>
<th>Name</th>
<th>Population</th>
</tr>
<#list cities as city>
<tr>
<td>${city.id}</td>
<td>${city.name}</td>
<td>${city.population}</td>
</tr>
</#list>
</table>
</body>
</html>
这是showCities.ftl
模板文件。 它使用 FreeMarker #list
宏显示所有城市对象。
resources/static/css/style.css
h2 {color: blue}
td:nth-child(3) {
text-align: right;
}
这是style.css
模板文件。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication
启用自动配置和组件扫描。
$ mvn spring-boot:run
我们运行该应用并定位到localhost:8080/myapp
地址。
在本教程中,我们介绍了一些基本的 Spring Boot 注解。
Spring Boot @ResponseBody
教程
在 Spring Boot @ResponseBody
教程中,我们将在控制器中使用 Spring @ResponseBody
注解将数据写入响应对象的主体。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。
WebJars 是打包到 JAR 文件中的客户端 Web 库(例如 jQuery 或 Bootstrap)。 它们允许轻松管理 Java Web 应用中的客户端依赖项
JQuery 是一个流行的开源 JavaScript 库,旨在简化 HTML 的客户端脚本。
Spring @ResponseBody
@ResponseBody
是一个 Spring 注解,它将方法返回值绑定到 Web 响应主体。 它不解释为视图名称。 它使用 HTTP 消息转换器根据请求 HTTP 标头中的内容类型将返回值转换为 HTTP 响应主体。
Spring @ResponseBody
示例
以下示例创建一个 Spring Boot Web 应用,该应用将 JSON 数据返回到客户端。 主页是通过 MVC 机制处理的; FreeMarker 用于创建主页的模板。 主页包含一个按钮,该按钮发送获取 JSON 数据的请求。 Spring Boot Web 应用在@ResponseBody
注解的帮助下以 JSON 格式发送数据。
pom.xml
src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ ├── model
│ │ │ └── City.java
│ │ ├── controller
│ │ │ └── MyController.java
│ │ └── service
│ │ ├── CityService.java
│ │ └── ICityService.java
│ └── resources
│ └── templates
│ └── index.ftl
└── test
└── java
这是应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootresponsebodyex</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
<version>0.34</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在spring-boot-starter-freemarker
是建立使用 FreeMarker 的观点 Spring MVC 的 Web 应用的启动。 我们将 Webjar 用于 JQuery。 webjars-locator
自动解析任何 WebJars 资产的版本。 该应用打包到 JAR 文件中,并使用 Tomcat 作为嵌入式 Web 服务器。
com/zetcode/model/City.java
package com.zetcode.model;
import java.util.Objects;
public class City {
private Long id;
private String name;
private int population;
public City() {
}
public City(Long id, String name, int population) {
this.id = id;
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
City city = (City) o;
return population == city.population &&
Objects.equals(id, city.id) &&
Objects.equals(name, city.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name, population);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("City{");
sb.append("id=").append(id);
sb.append(", name='").append(name).append('\'');
sb.append(", population=").append(population);
sb.append('}');
return sb.toString();
}
}
这是City
bean。 它具有id
,name
和population
属性。
com/zetcode/service/ICityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import java.util.List;
public interface ICityService {
List<City> findAll();
}
ICityService
包含获取所有城市的契约方法。
com/zetcode/service/CityService.java
package com.zetcode.service;
import com.zetcode.model.City;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class CityService implements ICityService {
@Override
public List<City> findAll() {
var cities = new ArrayList<City>();
cities.add(new City(1L, "Bratislava", 432000));
cities.add(new City(2L, "Budapest", 1759000));
cities.add(new City(3L, "Prague", 1280000));
cities.add(new City(4L, "Warsaw", 1748000));
cities.add(new City(5L, "Los Angeles", 3971000));
cities.add(new City(6L, "New York", 8550000));
cities.add(new City(7L, "Edinburgh", 464000));
cities.add(new City(8L, "Berlin", 3671000));
return cities;
}
}
CityService
返回八个城市对象。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import com.zetcode.bean.City;
import com.zetcode.service.ICityService;
import java.util.List;
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.ResponseBody;
@Controller
public class MyController {
@Autowired
ICityService cityService;
@RequestMapping(path = "/")
public String index() {
return "index";
}
@RequestMapping(path = "/GetCities", produces = "application/json; charset=UTF-8")
@ResponseBody
public List<City> findCities() {
var cities = (List<City>) cityService.findAll();
return cities;
}
}
控制器有两种方法。 index()
方法返回主页视图。 findCities()
方法返回城市列表作为 JSON 数据。
@Controller
public class MyController {
@Controller
注解表示我们有一个控制器类。
@RequestMapping(path = "/")
public String index() {
return "index";
}
index()
方法返回index
字符串,该字符串被解析为index.ftl
视图。 该视图位于src/main/resources/templates
目录中。 当 Spring 在 POM 文件中找到spring-boot-starter-freemarker
工件时,它将自动配置 FreeMarker。
@RequestMapping(path = "/GetCities", produces = "application/json; charset=UTF-8")
@ResponseBody
public List<City> findCities() {
var cities = (List<City>) cityService.findAll();
return cities;
}
对于GetCities
路径,将调用findCities()
方法。 produces
参数指示该方法返回 JSON 数据; Spring RequestResponseBodyMethodProcessor
通过使用HttpMessageConverter
写入响应主体来处理用@ResponseBody
注解的方法的返回值。 在我们的例子中,消息转换器是MappingJackson2HttpMessageConverter
,它使用 Jackson 的ObjectMapper
读取和写入 JSON。 (Jackson 是一个流行的 Java JSON 库。)
resources/templates/index.ftl
<!DOCTYPE html>
<html>
<head>
<title>Home Page</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="webjars/jquery/jquery.min.js"></script>
</head>
<body>
<button id="mybtn">Get cities</button>
<div>
<ul id="output">
</ul>
</div>
<script>
$('#mybtn').click(function () {
$.getJSON('GetCities', function (data) {
$("ul#output > li").remove();
$.each(data, function (key, value) {
$("#output").append('<li>' + value['name'] + " " + value['population'] + '</li>');
});
});
});
</script>
</body>
</html>
index.ftl
文件是主页的模板。 它包含一个按钮,该按钮对 Web 应用执行异步请求。 它将加载城市列表并将其写入 HTML 列表。
<script src="webjars/jquery/jquery.min.js"></script>
我们包括 JQuery 库。 多亏webjars-locator
,我们可以包括一个版本无关的 JQuery 库。 因此,如果 JQuery 的版本发生更改,我们不必更新链接。
<script>
$('#mybtn').click(function () {
$.getJSON('GetCities', function (data) {
$("ul#output > li").remove();
$.each(data, function (key, value) {
$("#output").append('<li>' + value['name'] + " " + value['population'] + '</li>');
});
});
});
</script>
通过$.getJSON()
方法,我们使用 HTTP GET 请求以 JSON 格式加载数据。 数据用$.each()
遍历并写入 HTML 列表。
在本教程中,我们在 Spring Boot Web 应用中使用了@ResponseBody
注解。 您可能也对相关教程感兴趣: Spring Boot ResponseEntity
教程, Spring Boot @PathVariable
教程, Spring Boot 上传文件, Spring Boot @RequestParam
教程,独立的 Spring 应用, Java 教程或列出所有 Spring Boot 教程。
Spring Boot ViewControllerRegistry
教程
Spring Boot ViewControllerRegistry
教程展示了如何使用ViewControllerRegistry
创建简单的路由。
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架的演进,可帮助您轻松创建独立的,生产级的基于 Spring 的应用。
ViewControllerRegistry
ViewControllerRegistry
允许创建预先配置了状态代码和/或视图的简单自动化控制器。
Spring Boot ViewControllerRegistry
示例
在下面的示例中,我们使用ViewControllerRegistry
创建一条简单路由。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ └───config
│ │ AppConfig.java
│ └───resources
│ ├───static
│ │ index.html
│ └───templates
│ hello.html
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>viewregistry</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Spring Boot 启动器是一组方便的依赖项描述符,可以极大地简化 Maven 配置。 spring-boot-starter-parent
具有 Spring Boot 应用的一些常用配置。 spring-boot-starter-web
是使用 Spring MVC 构建 Web(包括 RESTful)应用的入门工具。 它使用 Tomcat 作为默认的嵌入式容器。 spring-boot-starter-thymeleaf
是使用 Thymeleaf 视图构建 MVC Web 应用的入门工具。
在spring-boot-maven-plugin
提供了 Maven 的 Spring Boot 支持,使我们能够打包可执行的 JAR 或 WAR 档案。 它的spring-boot:run
目标运行 Spring Boot 应用。
com/zetcode/config/AppConfig.java
package com.zetcode.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class AppConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/hello").setViewName("hello");
}
}
在AppConfig
中,我们使用ViewControllerRegistry
的addViewController()
方法注册了一条新路由。
resources/templates/hello.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello page</title>
</head>
<body>
<p>
Hello there
</p>
</body>
</html>
hello.html
视图显示一条简单消息。
resources/static/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Home page</title>
</head>
<body>
<p>
This is home page. Go to <a href="hello">hello page</a>
</p>
</body>
</html>
这是一个主页。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
$ mvn spring-boot:run
应用运行后,我们可以导航到localhost:8080/
。
在本教程中,我们展示了如何使用 Spring ViewControllerRegistry
创建简单的路由。
Spring Boot @PathVariable
教程
Spring Boot @PathVariable
教程展示了如何读取带有@PathVariable
注解的 URL 模板变量。 我们创建一个 Spring Boot RESTful 应用来演示注解。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。
@PathVariable
@PathVariable
是一个 Spring 注解,它指示方法参数应绑定到 URI 模板变量。
它具有以下可选元素:
name
- 要绑定到的路径变量的名称required
- 指示路径变量是否为必需value
- 名称的别名
Spring Boot @PathVariable
示例
以下示例创建一个使用@PathVariable
的 Spring Boot Web 应用。 该应用接收一个 URL,从该 URL 构建到客户端的文本响应。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ └───controller
│ │ MyController.java
│ └───resources
└───test
└───java
这是 Spring Boot 应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>pathvariableex</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
这是 Maven 构建文件。 spring-boot-starter-web
是使用 Spring MVC 构建 Web 应用的入门工具。 它使用 Tomcat 作为默认的嵌入式容器。 spring-boot-devtools
是在开发 Spring Boot 应用时有用的构件。 它允许自动重启或实时重新加载应用。 该应用打包到一个 JAR 文件中。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@RequestMapping(path="/{name}/{age}")
public String getMessage(@PathVariable("name") String name,
@PathVariable("age") String age) {
var msg = String.format("%s is %s years old", name, age);
return msg;
}
}
控制器处理来自客户端的请求。 它从请求的 URL 中读取两个值。
@RestController
public class MyController {
我们有一个 RESTful Web 应用。
@RequestMapping(path="/{name}/{age}")
public String getMessage(@PathVariable("name") String name,
@PathVariable("age") String age) {
使用@PathVariable
注解,我们将请求 URL 模板路径变量绑定到方法变量。 例如,对于/July/28/
URL,七月值绑定到name
变量,而 28 值绑定到age
变量。
var msg = String.format("%s is %s years old", name, age);
return msg;
我们构建消息并返回。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
是设置 Spring Boot 应用的入口。 @SpringBootApplication
注解启用自动配置和组件扫描。
$ mvn spring-boot:run
我们启动 Spring Boot 应用。
$ curl localhost:8080/Robert/39/
Robert is 39 years old
我们使用curl
工具向应用创建请求。 该应用响应一条消息。 使用@PathVariable
从 URL 中提取值。
在本教程中,我们使用 Spring Boot 框架创建了一个 RESTful Web 应用。 我们已经演示了@PathVariable
的用法。 您可能也对相关教程感兴趣: Spring Boot @RequestParam
教程, Spring Boot @ResponseBody
教程, Spring Boot @RestController
教程, Java 教程,或列出所有 Spring Boot 教程。
Spring Boot REST Data JPA 教程
在本教程中,我们将使用 Data JPA 创建一个 Spring Boot RESTful 应用。
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架发展的下一步。 它有助于以最小的努力创建独立的,基于生产级的 Spring 应用。 它不再使用 XML 配置,并实现了约定而非配置原则。
H2 是 Java 开源关系数据库管理。 它可以在客户端-服务器模式下运行,也可以嵌入 Java 应用中。 H2 易于部署和安装,占地面积小。
Spring Data JPA 是总括性 Spring Data 项目的一部分,该项目使实现基于 JPA 的存储库变得更加容易。 Spring Data JPA 使用 JPA 将数据存储在关系数据库中。 它可以在运行时从存储库接口自动创建存储库实现。
RESTFul 应用遵循 REST 架构样式,该样式用于设计网络应用。 RESTful 应用生成对资源执行 CRUD(创建/读取/更新/删除)操作的 HTTP 请求。 RESTFul 应用通常以 JSON 或 XML 格式返回数据。
应用
以下应用是一个 Spring Boot RESTful 应用,它使用 Spring Data JPA 从 H2 数据库返回 JSON 格式的数据。
$ tree
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ ├── bean
│ │ │ └── City.java
│ │ ├── controller
│ │ │ └── MyController.java
│ │ ├── repository
│ │ │ └── CityRepository.java
│ │ └── service
│ │ ├── CityService.java
│ │ └── ICityService.java
│ └── resources
│ ├── application.yml
│ └── import.sql
└── test
└── java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>SpringBootRestDataJPA</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 h2
依赖项包括 H2 数据库驱动程序。
Spring Boot 启动器是一组方便的依赖项描述符,可以极大地简化 Maven 配置。 spring-boot-starter-parent
具有 Spring Boot 应用的一些常用配置。 spring-boot-starter-web
是使用 Spring MVC 构建 Web 应用的入门工具。 它使用 Tomcat 作为默认的嵌入式容器。 spring-boot-starter-data-jpa
是将 Spring Data JPA 与 Hibernate 结合使用的入门工具。
在spring-boot-maven-plugin
提供了 Maven 的 Spring Boot 支持,使我们能够打包可执行的 JAR 或 WAR 档案。 它的spring-boot:run
目标运行 Spring Boot 应用。
application.yml
server:
port: 8086
context-path: /rest
spring:
main:
banner-mode: "off"
jpa:
database: h2
hibernate:
dialect: org.hibernate.dialect.H2Dialect
ddl-auto: create-drop
logging:
level:
org:
springframework: ERROR
在application.yml
文件中,我们编写了 Spring Boot 应用的各种配置设置。 port
设置服务器端口和context-path
上下文路径(应用名称)。 完成这些设置后,我们可以通过localhost:8086/rest/
访问该应用。 使用banner-mode
属性,我们可以关闭 Spring 横幅。
JPA database
值指定要操作的目标数据库。 在本例中,我们指定了 Hibernate 方言org.hibernate.dialect.H2Dialect
。 ddl-auto
是数据定义语言模式; create-drop
选项将自动创建和删除数据库模式。
H2 数据库在内存中运行。 另外,我们将 spring 框架的日志记录级别设置为ERROR
。 在application.yml
文件位于中src/main/resources
目录。
City.java
package com.zetcode.bean;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "cities")
public class City {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int population;
public City() {
}
public City(String name, int population) {
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 13 * hash + Objects.hashCode(this.id);
hash = 13 * hash + Objects.hashCode(this.name);
hash = 13 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("City{id=").append(id).append(", name=")
.append(name).append(", population=")
.append(population).append("}");
return builder.toString();
}
}
这是City
实体。 每个实体必须至少定义两个注解:@Entity
和@Id
。 此前,我们已经设置了ddl-auto
选项,create-drop
这意味着 Hibernate 会创建这个实体表模式。
@Entity
@Table(name = "cities")
public class City {
@Entity
注解指定该类是一个实体,并映射到数据库表。 @Table
注解指定要用于映射的数据库表的名称。
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Id
注解指定实体的主键,@GeneratedValue
提供规范主键值的生成策略。
import.sql
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
模式是由 Hibernate 自动创建的。 之后,将执行import.sql
文件以将数据填充到表中。
CityRepository.java
package com.zetcode.repository;
import com.zetcode.bean.City;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CityRepository extends CrudRepository<City, Long> {
}
通过从 Spring CrudRepository
扩展,我们将为我们的数据存储库实现一些方法,包括findAll()
和findOne()
。 这样,我们节省了大量样板代码。
ICityService.java
package com.zetcode.service;
import com.zetcode.bean.City;
import java.util.List;
public interface ICityService {
public List<City> findAll();
public City findById(Long id);
}
ICityService
提供了获取所有城市并通过其 ID 从数据源获取城市的契约方法。
CityService.java
package com.zetcode.service;
import com.zetcode.bean.City;
import com.zetcode.repository.CityRepository;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CityService implements ICityService {
@Autowired
private CityRepository repository;
@Override
public List<City> findAll() {
List<City> cities = (List<City>) repository.findAll();
return cities;
}
@Override
public City findById(Long id) {
City city = repository.findOne(id);
return city;
}
}
CityService
包含findAll()
和findById()
方法的实现。 我们使用存储库从数据库检索数据。
@Autowired
private CityRepository repository;
注入CityRepository
。
List<City> cities = (List<City>) repository.findAll();
存储库的findAll()
方法返回城市列表。
City city = repository.findOne(id);
存储库的findOne()
方法返回一个由其 ID 标识的城市对象。
MyController.java
package com.zetcode.controller;
import com.zetcode.bean.City;
import com.zetcode.service.ICityService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@Autowired
ICityService cityService;
@RequestMapping("/cities")
public List<City> findCities() {
List<City> cities = (List<City>) cityService.findAll();
return cities;
}
@RequestMapping("/cities/{userId}")
public City findCity(@PathVariable Long userId) {
City city = cityService.findById(userId);
return city;
}
}
这是 Spring Boot RESTful 应用的控制器类。 @RestController
注解创建一个 RESTful 控制器。 传统的 MVC 控制器使用ModelAndView
,而 RESTful 控制器仅返回对象,并且对象数据通常以 JSON 或 XML 格式直接写入 HTTP 响应(通常)。
@Autowired
private ICityService cityService;
我们在countryService
字段中插入ICityService
。
@RequestMapping("/cities")
public List<City> findCities() {
List<City> cities = (List<City>) cityService.findAll();
return cities;
}
我们将带有/cities
路径的请求映射到控制器的findCities()
方法。 默认请求是 GET 请求。 该方法返回找到的所有城市的列表。
@RequestMapping("/cities/{userId}")
public City findCity(@PathVariable Long userId) {
City city = cityService.findById(userId);
return city;
}
在第二种方法中,我们返回一个特定的城市。 URL 路径包含要检索的城市的 ID; 我们使用@PathVariable
注解将 URL 模板变量绑定到cityId
参数。
我们不需要手动将City
域对象转换为 JSON。 因为 Jackson 2 在类路径中(通过spring-boot-starter-web
包含),所以 Spring 自动选择MappingJackson2HttpMessageConverter
将City
实例转换为 JSON。
Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
$ mvn spring-boot:run
使用mvn spring-boot:run
命令,运行应用。 该应用部署在嵌入式 Tomcat 服务器上。
$ curl localhost:8086/rest/cities
[{"id":1,"name":"Bratislava","population":432000},
{"id":2,"name":"Budapest","population":1759000},
{"id":3,"name":"Prague","population":1280000},
{"id":4,"name":"Warsaw","population":1748000},
{"id":5,"name":"Los Angeles","population":3971000},
{"id":6,"name":"New York","population":8550000},
{"id":7,"name":"Edinburgh","population":464000},
{"id":8,"name":"Berlin","population":3671000}]
使用curl
命令,我们可以获得所有城市。
$ curl localhost:8086/rest/cities/1
{"id":1,"name":"Bratislava","population":432000}
在这里,我们得到了一个由其 ID 标识的城市。
在本教程中,我们已从 Spring Boot RESTful 应用以 JSON 格式将数据返回给客户端。 我们使用 Spring Data JPA 从 H2 数据库检索数据。 您可能也对相关教程感兴趣:
- Spring Boot CSV 教程
- Spring Boot Data JPA
@Query
教程 - Spring Boot Data JPA
@NamedQuery
教程 - 在 Spring Boot 中加载资源
- Spring Boot H2 REST 教程
- Spring Boot Thymeleaf 教程
- Spring Boot Mustache 教程
- Spring Boot Swing 集成教程
- Spring Web 应用介绍
- Spring Boot RESTFul 应用
Spring Boot @RequestParam
教程
在本教程中,我们将在控制器中使用@RequestParam
注解来读取请求参数。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。
Spring @RequestParam
@RequestParam
是一个 Spring 注解,用于将 Web 请求参数绑定到方法参数。
它具有以下可选元素:
defaultValue
- 当没有提供请求参数或值为空时用作备用name
- 要绑定到的请求参数的名称required
- 告诉参数是否为必需value
- 名称的别名
Spring @RequestParam
示例
以下示例创建一个使用@RequestParam
的 Spring Boot Web 应用。 我们有一个带有两个标签的 HTML 表单:文本输入和复选框。 这两个标记创建请求参数,该参数在控制器中通过@RequestParam
读取。
$ tree
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ └── controller
│ │ └── MyController.java
│ └── resources
│ └── static
│ └── index.html
└── test
└── java
这是 Spring Boot 应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>requestparamex</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在 Maven 构建文件中,我们有spring-boot-starter-web
,它是使用 Spring MVC 构建 Web 应用的入门。 它使用 Tomcat 作为默认的嵌入式容器。 spring-boot-devtools
是在开发 Spring Boot 应用时有用的构件。 它允许自动重启或实时重新加载应用。 该应用打包到一个 JAR 文件中。
com/zetcode/MyController.java
package com.zetcode.controller;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MyController {
@RequestMapping(path="/message", produces=MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
public String processForm(@RequestParam(defaultValue="Guest") String name,
@RequestParam(required = false) String adult) {
var greet = "on".equals(adult) ? "Good morning" : "Hi";
var message = String.format("%s %s!", greet, name);
return message;
}
}
控制器处理 HTML 表单。 它从请求中读取两个参数。
@Controller
public class MyController {
在 Spring 中,控制器类使用@Controller
注解进行注解。
@RequestMapping(path="/message", produces=MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
processForm()
方法映射到/message
路径并返回纯文本。 @ResponseBody
注解指示方法返回值已绑定到 Web 响应正文。
public String processForm(@RequestParam(defaultValue="Guest") String name,
@RequestParam(required = false) String adult) {
使用@RequestParam
注解,我们将请求参数绑定到方法变量。 如果参数不可用(文本输入为空),则defaultValue
选项将提供默认值。 required
选项表明该参数是必需的。 该方法重新调整字符串。
var greet = "on".equals(adult) ? "Good morning" : "Hi";
var message = String.format("%s %s!", greet, name);
return message;
我们构建消息并返回。
resources/static/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Home page</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<form action="message">
<div>
<label>Name:</label>
<input type="text" name="name">
</div>
<div>
<label><input type="checkbox" name="adult">Adult</label>
</div>
<button type="submit">Submit</button>
</form>
</body>
</html>
index.html
文件是主页。 该文件位于src/main/resources/static
目录中,Spring Boot 在该目录中需要静态资源,例如 HTML 或 CSS 文件。 我们有一个带有输入文本和复选框标签的简单 HTML 表单。
<form action="message">
action
选项包含在控制器方法映射中使用的字符串。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
是设置 Spring Boot 应用的入口。 @SpringBootApplication
注解启用自动配置和组件扫描。
$ mvn spring-boot:run
应用运行后,我们可以导航到localhost:8080
。
在本教程中,我们使用 Spring Boot 框架创建了 Web 应用。 我们已经演示了@RequestParam
的用法。 您可能也对相关教程感兴趣: Spring Boot @PathVariable
教程, Spring Boot @ResponseBody
教程, Java 教程或列出所有 Spring Boot 教程。
Spring Boot 列出 bean
在本教程中,我们列出了存储在 Spring 容器中的所有 bean,包括内置和自定义 bean。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可帮助您以最少的精力创建独立的,生产级的基于 Spring 的应用。
Spring 核心容器创建并管理 Bean。 可以通过ApplicationContext
获得这些 bean。 在以下应用中,我们列出了所有存储的 bean。 该应用是命令行 Spring Boot 应用。
$ tree
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ ├── bean
│ │ │ └── MyBean.java
│ │ └── MyRunner.java
│ └── resources
└── test
└── java
这是 Spring Boot 应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>SpringBootListBeans</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<name>SpringBootListBeans</name>
</project>
这是 Maven 构建文件。 spring-boot-starter
是包括自动配置支持,日志记录和 YAML 在内的核心启动器。 该应用打包到一个 JAR 文件中。
MyBean.java
package com.zetcode.bean;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
private final String message = "This is MyBean";
public String getMessage() {
return message;
}
}
MyBean
是由 Spring 创建和管理的自定义 bean。 Spring 自动检测由@Component
注解修饰的类,并将其存储在 Spring 容器中。
MyRunner.java
package com.zetcode;
import com.zetcode.bean.MyBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
@Autowired
private ApplicationContext appContext;
@Autowired
private MyBean myBean;
@Override
public void run(String... args) throws Exception {
System.out.println(myBean.getMessage());
System.out.println("List of beans:");
String[] beans = appContext.getBeanDefinitionNames();
for (String bean : beans) {
System.out.println(bean);
}
}
}
CommandLineRunner
接口指示当SpringApplication
中包含 bean 时应运行它。 它可以用来创建 Spring Boot 命令行应用。
@Component
public class MyRunner implements CommandLineRunner {
MyRunner
也是一个 Spring bean,并在 bean 中列出。
@Autowired
private ApplicationContext appContext;
ApplicationContext
将以@Autowired
注解注入到字段中。
@Autowired
private MyBean myBean;
同样,我们注入自定义 bean。
System.out.println(myBean.getMessage());
在这里,我们打印存储在自定义 bean 中的消息。
String[] beans = appContext.getBeanDefinitionNames();
从应用上下文中,我们获得一个带有getBeanDefinitionNames()
的 bean 名称数组。
for (String bean : beans) {
System.out.println(bean);
}
Bean 名称将打印到控制台。
Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
在Application
中,设置 Spring Boot 应用。 设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。 Spring 将自动扫描 Bean,并同时拾取MyBean
和MyRunner
。
$ mvn spring-boot:run -q
...
This is MyBean
List of beans:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
application
org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory
myRunner
myBean
...
我们运行该应用。 -q
Maven 选项关闭 Maven 消息。
在本教程中,我们列出了存储在 Spring 容器中的所有 bean。 您可能也对相关教程感兴趣: Spring Boot Bean , Spring Boot @Qualifier
注解,在 Spring Boot 中提供静态内容, Spring Boot DataSourceBuilder
教程, Spring Boot iText 教程, Java 教程。
Spring Boot @Bean
在 Spring Boot @Bean
教程中,我们使用@Bean
注解在 Spring Boot 框架中创建一个简单的 Bean。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可帮助您以最少的精力创建独立的,生产级的基于 Spring 的应用。
Spring @Bean
注解
Spring @Bean
注解告诉一个方法产生一个由 Spring 容器管理的 bean。 它是方法级别的注解。 在 Java 配置(@Configuration
)期间,将执行该方法并将其返回值注册为BeanFactory
中的 Bean。
Spring Boot @Bean
示例
Spring 核心容器创建并管理 Bean。 在以下应用中,我们演示如何创建带有@Bean
注解的 Spring bean。 该应用是命令行 Spring Boot 应用。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ Application.java
│ │ AppName.java
│ │
│ └───resources
│ application.properties
│ logback.xml
│
└───test
└───java
这是 Spring Boot 应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootbean</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 spring-boot-starter
是包括自动配置支持,日志记录和 YAML 在内的核心启动器。 该应用打包到一个 JAR 文件中。
com/zetcode/AppName.java
package com.zetcode;
interface AppName {
String getName();
}
我们有一个定义契约的简单接口。 它用于创建返回应用名称的匿名类。
resources/application.properties
spring.main.banner-mode=off
app.name=SpringBootBean
application.properties
文件包含应用配置设置。 有一些内置的应用属性,我们可以创建自定义属性。 spring.main.banner-mode
属性是 Spring 内置属性; 我们关闭了 Spring 的标志。 app.name
是我们的自定义属性,其中包含应用名称。
resources/logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<logger name="org.springframework" level="ERROR"/>
<logger name="com.zetcode" level="INFO"/>
</configuration>
在logback.xml
文件中,我们配置应用日志记录。 我们将日志记录级别设置为ERROR
。 这样,我们的输出就不会充满不必要的信息。 spring-boot-starter
依赖项启用登录日志记录。
com/zetcode/Application.java
package com.zetcode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Application implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
@Autowired
private AppName appName;
@Bean
public AppName getAppName(@Value("${app.name}") String appName) {
return () -> appName;
}
@Override
public void run(String... args) throws Exception {
logger.info("Application name: {}", appName.getName());
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
在Application
中,我们创建一个 bean,调用其方法并设置 Spring Boot 应用。 CommandLineRunner
接口指示当SpringApplication
中包含 bean 时应运行它。 它可以用来在 Spring Boot 中创建命令行应用。
@SpringBootApplication
public class Application implements CommandLineRunner {
@SpringBootApplication
注解启用自动配置和组件扫描。
@Autowired
private AppName appName;
使用@Autowired
注解,我们将AppName
bean 注入到字段中。
@Bean
public AppName getAppName(@Value("${app.name}") String appName) {
return () -> appName;
}
在这里,我们创建AppName
bean; 该 bean 由 Spring 容器管理。 尽管@Component
注解用于装饰由 Spring 扫描自动检测的类,但@Bean
注解用于显式声明 bean 创建。
@Value
注解用于将app.name
属性的值设置为appName
参数。
logger.info("Application name: {}", appName.getName());
我们调用 bean 的getName()
方法。
我们使用mvn spring-boot:run
运行该应用。
在本教程中,我们创建了带有@Bean
注解的 Spring bean。 您可能也对相关教程感兴趣: Spring Boot @Lazy
教程, Java 教程或列出 Spring Boot 教程。
Spring Boot @Qualifier
教程
Spring Boot @Qualifier
教程展示了如何使用@Qualifier
来区分相同类型的 bean。 它也可以用于注解其他自定义注解,这些注解随后可以用作限定符。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可帮助您以最少的精力创建独立的,生产级的基于 Spring 的应用。
以下三个应用是命令行 Spring Boot 应用。
@Qualifier
Person
bean
在我们的应用中,我们有两个Person
类型的 bean:Student
和Manager
。 我们使用@Qualifier
注解来区分它们。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ └───model
│ │ Manager.java
│ │ Person.java
│ │ Student.java
│ └───resources
└───test
└───java
这是 Spring Boot 应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootqualifier</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 spring-boot-starter
是包括自动配置支持,日志记录和 YAML 在内的核心启动器。 该应用打包到一个 JAR 文件中。
com/zetcode/model/Person.java
package com.zetcode.model;
public interface Person {
String info();
}
我们有一个定义Person
类型的接口。
com/zetcode/model/Student.java
package com.zetcode.model;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
@Qualifier("student")
public class Student implements Person {
@Override
public String info() {
return "Student";
}
}
Student
继承自Person
。 @Component
是基本的 Spring 注解,它允许 Spring 容器检测Student
。 @Qualifier("student")
用"student"
字符串唯一标识此 bean。
com/zetcode/model/Manager.java
package com.zetcode.model;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
@Qualifier("manager")
public class Manager implements Person {
@Override
public String info() {
return "Manager";
}
}
我们还有另一个名为Manager
的 bean。 该 bean 也用@Qualifier("manager")
注解标识。
com/zetcode/MyRunner.java
package com.zetcode;
import com.zetcode.model.Person;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);
@Autowired
@Qualifier("student")
private Person p1;
@Autowired
@Qualifier("manager")
private Person p2;
@Override
public void run(String... args) throws Exception {
logger.info("{}", p1.info());
logger.info("{}", p2.info());
}
}
CommandLineRunner
接口指示当SpringApplication
中包含 bean 时应运行它。 它可以用来在 Spring Boot 中创建命令行应用。
@Component
public class MyRunner implements CommandLineRunner {
CommandLineRunner
也是一个 Spring bean,并用@Component
注解装饰。 它由 Spring 自动检测。
@Autowired
@Qualifier("student")
private Person p1;
我们将Person
bean 注入p1
字段。 @Qualifier("student")
指定它是一个Student
bean。
@Autowired
@Qualifier("manager")
private Person p2;
同样,我们将Manager
bean 注入p2
字段。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
注解启用自动配置和组件扫描。
使用工厂创建 bean
在第二个应用中,我们使用工厂类来生成 bean。 pom.xml
,Person.java
,Application.java
和MyRunner.java
保持不变。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ ├───conf
│ │ │ PersonFactory.java
│ │ └───model
│ │ Manager.java
│ │ Person.java
│ │ Student.java
│ └───resources
└───test
└───java
这是项目结构。
com/zetcode/model/Manager.java
package com.zetcode.model;
public class Manager implements Person {
@Override
public String info() {
return "Manager";
}
}
注解已从Manager
类中删除。
com/zetcode/model/Student.java
package com.zetcode.model;
public class Student implements Person {
@Override
public String info() {
return "Student";
}
}
同样,Student
类没有注解。
com/zetcode/conf/PersonFactory.java
package com.zetcode.conf;
import com.zetcode.model.Manager;
import com.zetcode.model.Person;
import com.zetcode.model.Student;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PersonFactory {
@Bean
@Qualifier("student")
public Person createStudent() {
return new Student();
}
@Bean
@Qualifier("manager")
public Person createManager() {
return new Manager();
}
}
在前面的示例中,Spring 会自动检测到这些 bean。 在这里,PersonFactory
借助@Bean
注解创建了两个 bean。
@Bean
@Qualifier("student")
public Person createStudent() {
return new Student();
}
@Bean
注解标记了定义 bean 的方法。 @Qualifier("student")
指示要创建Person
的哪个实现。
创建自定义@Qualifier
注解
为了减少代码,我们可以创建自定义@Qualifier
注解。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ ├───conf
│ │ │ PersonFactory.java
│ │ ├───model
│ │ │ Manager.java
│ │ │ Person.java
│ │ │ Student.java
│ │ └───qualifier
│ │ PersonQ.java
│ └───resources
└───test
└───java
这是项目结构; 我们列出了第一个应用中列出的pom.xml
以外的所有文件。
com/zetcode/model/Person.java
package com.zetcode.model;
public interface Person {
String info();
}
这是Person
类型。
com/zetcode/model/Manager.java
package com.zetcode.model;
import org.springframework.stereotype.Component;
@Component
public class Manager implements Person {
@Override
public String info() {
return "Manager";
}
}
Manager
类装饰有@Component
注解; 它将由 Spring 自动检测。
com/zetcode/model/Student.java
package com.zetcode.model;
import org.springframework.stereotype.Component;
@Component
public class Student implements Person {
@Override
public String info() {
return "Student";
}
}
Student
的情况相同。
com/zetcode/qualifier/PersonQ.java
package com.zetcode.qualifier;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.annotation.Qualifier;
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface PersonQ {
String value();
}
在这里,我们定义了一个新的@PersonQ
限定符。
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Targer
注解指出可以在何处应用注解。 在我们的情况下,它可以应用于字段,方法和参数。
@Retention(RetentionPolicy.RUNTIME)
@Retention
注解指定标记的注解的存储方式。 使用RetentionPolicy.RUNTIME
,标记的注解将由 JVM 保留,因此可以由运行时环境使用。
public @interface PersonQ {
@interface
关键字用于声明新的注解类型。
com/zetcode/conf/PersonFactory.java
package com.zetcode.conf;
import com.zetcode.model.Manager;
import com.zetcode.model.Person;
import com.zetcode.model.Student;
import com.zetcode.qualifier.PersonQ;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PersonFactory {
@PersonQ("student")
public Person createStudent() {
return new Student();
}
@PersonQ("manager")
public Person createManager() {
return new Manager();
}
}
在PersonFactory
中,我们使用@PersonQ
识别创建了哪种类型的 bean。
com/zetcode/MyRunner.java
package com.zetcode;
import com.zetcode.model.Person;
import com.zetcode.qualifier.PersonQ;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);
@Autowired
@PersonQ("student")
private Person p1;
@Autowired
@PersonQ("manager")
private Person p2;
@Override
public void run(String... args) throws Exception {
logger.info("{}", p1.info());
logger.info("{}", p2.info());
}
}
在MyRunner
中,我们注入带有@Autowired
和@PersonQ
注解的 bean。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
在Application
中,我们设置了 Spring Boot 应用。
您可能也对 Java 教程感兴趣,或列出所有 Spring Boot 教程。
在 Spring Boot 中提供静态内容
Spring Boot 静态内容显示了如何在 Spring Boot 应用中提供静态内容。
Spring 是流行的 Java 应用框架。 Spring Boot 致力于以最小的努力创建独立的,基于生产级别的基于 Spring 的应用。
Spring Boot 自动添加位于以下任何目录中的静态 Web 资源:
/META-INF/resources/
/resources/
/static/
/public/
目录位于类路径或ServletContext
的根目录中。
在我们的应用中,我们有一个 HTML 文件,其中包含一个简单的链接。 该链接触发来自 Web Boot 应用的响应。 它返回纯文本消息。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ ├───controller
│ │ │ MyController.java
│ │ └───model
│ │ Message.java
│ └───resources
│ │ application.properties
│ └───static
│ │ index.html
│ └───css
│ main.css
└───test
└───java
└───com
└───zetcode
└───controller
MyControllerTest.java
这是 Spring Boot 应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootstaticex</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 spring-boot-starter-web
是使用 Spring MVC 构建 Web 应用的入门。 spring-boot-starter-test
导入必要的测试模块。 该应用打包到一个 JAR 文件中。
com/zetcode/model/Message.java
package com.zetcode.model;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Message {
@Value("${app.message}")
private String message;
public String get() {
return message;
}
}
Message
设置 Spring Boot 应用。
@Value("${app.message}")
private String message;
我们将application.properties
中的值注入message
变量中。
resources/application.properties
app.message=Hello there
application.properties
文件包含 Spring Boot 应用的各种配置设置。 我们定义一个具有文本消息的自定义属性。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import com.zetcode.model.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MyController {
@Autowired
private Message message;
@GetMapping(path = "/message")
@ResponseBody
public String message() {
return message.get();
}
}
这是 Spring Boot Web 应用的控制器类。 控制器以@Controller
注解修饰。 控制器具有一个映射; 它被映射到/message
路径并返回纯文本消息。
@Autowired
private Message message;
Message
对象被注入到属性中。
@GetMapping(path = "/message")
@ResponseBody
public String message() {
return message.get();
}
message()
方法响应 GET 请求。 @ResponseBody
注解将字符串值放入 Web 响应正文。
resources/static/index.html
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="css/main.css" rel="stylesheet" type="text/css">
</head>
<body>
<h2>Home page</h2>
<a href="/message">Get message</a>
</body>
</html>
在index.html
文件中,我们必须调用从 Web 应用的响应的链接。 该文件位于src/main/resources/static
目录中,该目录是 Spring 寻找静态内容的默认目录。
<link href="css/main.css" rel="stylesheet" type="text/css">
在链接标记中,我们指的是main.css
静态资源,该资源位于src/main/resources/static/css
目录中。
resources/static/css/main.css
h2 { color: blue }
在main.css
文件中,我们将h2
标签设置为蓝色。
com/zetcode/controller/MyControllerTest.java
package com.zetcode.controller;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void getHome() throws Exception {
this.mockMvc.perform(get("/"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(forwardedUrl("index.html"));
}
@Test
public void getMessage() throws Exception {
this.mockMvc.perform(get("/message"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string("Hello there"));
}
}
在MyControllerTest
中,我们有两个测试:一个用于主页,另一个用于返回的消息文本。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
注解启用自动配置和组件扫描。
在本教程中,我们在 Spring Boot 应用中提供了静态上下文。 您可能也对相关教程感兴趣: Spring Boot DataSourceBuilder
教程, Spring Boot iText 教程, Spring Boot RESTFul 应用, Spring Web 应用简介,独立的 Spring 应用, Java 教程或列出所有 Spring Boot 教程。
Spring Boot Whitelabel 错误
Spring Boot Whitelabel 错误教程展示了如何在 Spring Boot 应用中配置和显示错误消息。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。
WhiteLabel 错误页面
WhiteLabel 错误页面是通用的 Spring Boot 错误页面,当不存在自定义错误页面时显示。
server.error.whitelabel.enabled=false
通过将server.error.whitelabel.enabled
设置为false
,可以在application.properties
文件中禁用 WhiteLabel 错误。
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
禁用 WhiteLabel 错误的另一种方法是排除ErrorMvcAutoConfiguration
。
@SpringBootApplication(exclude = {ErrorMvcAutoConfiguration.class})
public class Application {
或者,可以在注解中进行排除。
当禁用 WhiteLabel 错误页面并且未提供自定义错误页面时,将显示 Web 服务器的错误页面(Tomcat,Jetty)。
Spring Boot 自定义错误页面
如果不使用 Thymeleaf 的模板引擎,我们可以在一个src/main/resources/public/errors
目录放置一个普通的自定义错误页。
resources/public/errors/404.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404 - resource not found</title>
</head>
<body>
<h2>404 - Resource not found</h2>
<p>
The requested resource was not found; - public
</p>
</body>
</html>
这是一个 404 错误一般错误页面。
resources/templates/error.html
<!DOCTYPE html>
<html>
<head>
<title>Error occurred</title>
</head>
<body>
<h1>Error occurred</h1>
<p>
An error has occurred. Please contact the administrator; - template generic
</p>
</body>
</html>
可以将使用模板的通用错误页面放置在src/main/resources/templates/
目录中。
resources/templates/error/404.html
<!DOCTYPE html>
<html lang="en">
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>404 - resource not found</title>
</head>
<body>
<h2>404 - Resource not found</h2>
<p>
The requested resource was not found; template - specific
</p>
<p th:text="${error}">Error Info</p>
<p th:text="${status}">Status</p>
</body>
</html>
可以将使用模板的特定错误页面放在src/main/resources/templates/error/
目录中。
Spring Boot 自定义错误页面示例
在下面的示例中,我们创建一个简单的 Spring Boot 应用,其中使用针对 404 错误的自定义错误页面。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ └───controller
│ │ MyController.java
│ └───resources
│ │ application.properties
│ └───templates
│ └───error
│ 404.html
└───test
└───java
这是 Spring 应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootwhitelabelerror</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
</parent>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven pom.xml
文件。 我们有spring-boot-starter-web
和spring-boot-starter-thymeleaf
。
resources/application.properties
#server.error.whitelabel.enabled=false
#spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
在application.properties
中,我们可以使用这些设置中的on
来关闭 WhiteLabel 错误。 如果我们提供一个自定义错误页面,它将自动优先于 WhiteLabel 错误。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@GetMapping("/")
public String home() {
return "Home page";
}
}
我们有一个简单的控制器,用于返回首页的文本消息。
resources/templates/error/404.html
<!DOCTYPE html>
<html lang="en">
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>404 - resource not found</title>
</head>
<body>
<h2>404 - Resource not found</h2>
<p>
The requested resource was not found; template - specific
</p>
<p th:text="${error}">Error Info</p>
<p th:text="${status}">Status</p>
</body>
</html>
这是使用 Thymeleaf 创建的自定义模板错误页面。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
这段代码设置了 Spring Boot 应用。
在本教程中,我们介绍了 WhiteLabel 错误,并展示了如何创建自定义错误页面。
Spring Boot DataSourceBuilder
教程
Spring Boot DataSourceBuilder
教程展示了如何使用DataSourceBuilder
在命令行 Spring Boot 应用中创建数据源。 使用了 HikariCP 连接池。
DataSourceBuilder
是 Java 便利类,用于创建具有常见实现和属性的数据源。
H2 是完全用 Java 创建的开源关系数据库管理系统。 它可以嵌入 Java 应用中或以客户端-服务器模式运行。 它易于部署和安装,占地面积小。
Spring 是用于开发 Java 企业应用的 Java 应用框架。 它还有助于集成各种企业组件。 Spring Boot 使创建具有 Spring 动力的生产级应用和服务变得很容易,而对安装的要求却最低。
Spring Boot DataSourceBuilder
示例
以下是一个简单的 Spring Boot 控制台应用。 它从 H2 内存数据库中检索数据并将其显示在终端中。 要配置数据源,我们使用DataSourceBuilder
类。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ ├───config
│ │ │ AppConfig.java
│ │ └───model
│ │ Car.java
│ └───resources
│ application.yaml
│ data-h2.sql
│ logback.xml
│ schema-h2.sql
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>datasourcebuilder</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Maven pom.xml
我们声明必要的依赖关系。
com/zetcode/model/Car.java
package com.zetcode.model;
import java.util.Objects;
public class Car {
private Long id;
private String name;
private int price;
public Car() {}
public Car(Long id, String name, int price) {
this.id = id;
this.name = name;
this.price = price;
}
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 int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return price == car.price &&
Objects.equals(id, car.id) &&
Objects.equals(name, car.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name, price);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Car{");
sb.append("id=").append(id);
sb.append(", name='").append(name).append('\'');
sb.append(", price=").append(price);
sb.append('}');
return sb.toString();
}
}
这是Car
bean 类。 它包含商品 ID,名称和价格。
resources/schema-h2.sql
CREATE TABLE cars(id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255), price INT);
该 SQL 脚本创建cars
表。
resources/data-h2.sql
INSERT INTO cars(name, price) VALUES('Audi', 52642);
INSERT INTO cars(name, price) VALUES('Mercedes', 57127);
INSERT INTO cars(name, price) VALUES('Skoda', 9000);
INSERT INTO cars(name, price) VALUES('Volvo', 29000);
INSERT INTO cars(name, price) VALUES('Bentley', 350000);
INSERT INTO cars(name, price) VALUES('Citroen', 21000);
INSERT INTO cars(name, price) VALUES('Hummer', 41400);
INSERT INTO cars(name, price) VALUES('Volkswagen', 21600);
该脚本用数据填充表。 这两个脚本都位于类路径的根目录中。
resources/logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<logger name="org.springframework" level="ERROR"/>
<logger name="com.zetcode" level="DEBUG"/>
<logger name="com.zaxxer.hikari" level="INFO"/>
</configuration>
在logback.xml
文件中,我们配置日志记录级别。 我们将 Spring 框架的日志记录级别设置为ERROR
,以便我们的输出不会被不必要的细节所困扰。
resources/application.yml
datasource:
hikari:
minimum-idle: 1
maximum-pool-size: 20
jdbcUrl: jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
driverClassName: org.h2.Driver
spring:
main:
banner-mode: "off"
datasource:
platform: h2
Spring Boot 的主要配置文件称为application.yml
。 在datasource
属性中,我们配置数据源和 HikariCP。 我们使用内存中的 H2 数据库。
使用banner-mode
属性,我们关闭 Spring Boot 横幅。 该平台值用在 SQL 初始化脚本中:schema-${platform}.sql
和data-${platform}.sql
。
com/zetcode/config/AppConfig.java
package com.zetcode.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@Configuration
public class AppConfig {
@Bean
@Primary
@ConfigurationProperties(prefix = "datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
在AppConfig
中生成一个数据源。 使用@ConfigurationProperties
注解,我们已将配置外部化到 YAML 文件中。
com/zetcode/MyRunner.java
package com.zetcode;
import com.zetcode.model.Car;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void run(String... args) {
var sql = "SELECT * FROM cars";
var cars = jdbcTemplate.query(sql, BeanPropertyRowMapper.newInstance(Car.class));
for (Car car: cars) {
System.out.println(car);
}
}
}
MyRunner
执行 SQL 查询并在控制台中显示输出。
@Autowired
private JdbcTemplate jdbcTemplate;
注入JdbcTemplate
。
var sql = "SELECT * FROM cars";
这是要执行的 SQL。 我们从cars
表中选择所有汽车。
var cars = jdbcTemplate.query(sql, BeanPropertyRowMapper.newInstance(Car.class));
BeanPropertyRowMapper
将一行转换为指定映射目标类的新实例。
for (Car car: cars) {
System.out.println(car);
}
我们遍历所有汽车对象,并将它们打印到控制台。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。
$ mvn -q spring-boot:run
...
Car{id=1, name='Audi', price=52642}
Car{id=2, name='Mercedes', price=57127}
Car{id=3, name='Skoda', price=9000}
Car{id=4, name='Volvo', price=29000}
Car{id=5, name='Bentley', price=350000}
Car{id=6, name='Citroen', price=21000}
Car{id=7, name='Hummer', price=41400}
Car{id=8, name='Volkswagen', price=21600}
...
我们运行 Spring Boot 应用。 显示八辆车。
在本教程中,我们在 Spring Boot 控制台应用中使用了 DataSourceBuilder。 您可能也对这些相关教程感兴趣: Spring Boot MySQL 教程, Spring Boot PostgreSQL 教程, Spring Boot H2 教程, Java 教程或列出所有 Spring Boot 教程。
Spring Boot H2 教程
在 Spring Boot H2 教程中,我们展示了如何在 Spring Boot 中使用嵌入式 H2 内存数据库。 创建一个简单的 REST 应用。
Spring 是用于开发 Java 企业应用的 Java 应用框架。 它还有助于集成各种企业组件。 Spring Boot 使创建具有 Spring 动力的生产级应用和服务变得很容易,而对安装的要求却最低。
Jetty 是一个开源项目,提供 HTTP 服务器,HTTP 客户端和 Java Servlet 容器。 该项目是 Eclipse Foundation 的一部分。 Jetty 是一个成熟的项目,始于 1995 年。Jetty 可以轻松地嵌入到设备,工具,框架,应用服务器和群集中。
H2 是完全用 Java 创建的开源关系数据库管理系统。 它可以嵌入 Java 应用中或以客户端-服务器模式运行。 它易于部署和安装,占地面积小。
JdbcTemplate
是一个 Spring 库,可以帮助程序员创建与关系数据库和 JDBC 一起使用的应用。 它会处理许多繁琐且容易出错的底层细节,例如处理事务,清理资源以及正确处理异常。 JdbcTemplate
在 Spring 的spring-jdbc
模块中提供。
JSON(JavaScript 对象表示法)是一种轻量级的数据交换格式。 人类可以轻松地进行读写,并通过机器解析并生成 JSON。 JSON 的官方互联网媒体类型为application/json
。 JSON 文件扩展名是.json
。
RESTFul 应用遵循 REST 架构样式,该样式用于设计网络应用。 RESTful 应用生成 HTTP 请求,这些请求对资源执行 CRUD(创建/读取/更新/删除)操作。
Spring Boot RESTFul 应用
H2 可以轻松地与 Spring Boot 一起使用。 当 Spring Boot 在 POM 文件中检测到 H2 时,它会自动为该应用配置内存 H2 数据库。
以下是具有 RESTFul 服务的简单 Spring Boot 应用。 该应用与嵌入式 Jetty 服务器一起运行。
$ tree
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── bean
│ │ │ └── Car.java
│ │ ├── main
│ │ │ └── Application.java
│ │ ├── service
│ │ │ ├── CarService.java
│ │ │ └── ICarService.java
│ │ └── web
│ │ └── MyController.java
│ └── resources
│ ├── application.yml
│ ├── data-h2.sql
│ └── schema-h2.sql
└── test
└── java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>SpringBootH2</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Maven pom.xml
文件包含 Jetty,H2 驱动程序和 Spring 框架的依赖项。
Car.java
package com.zetcode.bean;
public class Car {
private Long id;
private String name;
private int price;
public Car() {}
public Car(Long id, String name, int price) {
this.id = id;
this.name = name;
this.price = price;
}
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 int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" + "id=" + id + ", name=" + name + ", price=" + price + '}';
}
}
这是Car
bean 类。 它包含商品 ID,名称和价格。
application.yml
spring:
datasource:
platform: h2
application.yml
是主要的 Spring Boot 配置文件。 该平台值用于 SQL 初始化脚本:schema-${platform}.sql
和data-${platform}.sql
中。
请注意,我们没有配置数据源。 这是因为,如果没有配置数据,Spring 会以内存模式自动配置 H2。 我们希望有一个内存数据库,因此我们让 Spring 进行自动配置。
schema-h2.sql
CREATE TABLE Cars(ID BIGINT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(100), PRICE INT);
该 SQL 脚本创建Cars
表。
data-h2.sql
INSERT INTO Cars(Name, Price) VALUES('Audi', 52642);
INSERT INTO Cars(Name, Price) VALUES('Mercedes', 57127);
INSERT INTO Cars(Name, Price) VALUES('Skoda', 9000);
INSERT INTO Cars(Name, Price) VALUES('Volvo', 29000);
INSERT INTO Cars(Name, Price) VALUES('Bentley', 350000);
INSERT INTO Cars(Name, Price) VALUES('Citroen', 21000);
INSERT INTO Cars(Name, Price) VALUES('Hummer', 41400);
INSERT INTO Cars(Name, Price) VALUES('Volkswagen', 21600);
该脚本用数据填充表。 这两个脚本都位于类路径的根目录中。
MyController.java
package com.zetcode.web;
import com.zetcode.bean.Car;
import com.zetcode.service.ICarService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@Autowired
private ICarService carService;
@RequestMapping("/")
public String index(Model model) {
return "Home page";
}
@RequestMapping(name = "/cars", method = RequestMethod.GET)
public List<Car> cars() {
List<Car> cars = carService.findAll();
return cars;
}
}
在MyController
中,我们有两种方法可以响应两个请求。 传统的 MVC 控制器使用视图技术执行 HTML 的服务器端渲染。 RESTful Web 服务控制器将数据写入 HTTP 响应。 默认格式为 JSON。
@RestController
public class MyController {
@RestController
注解在 Spring 中创建 RESTFul Web 服务。
@Autowired
private ICarService carService;
我们将CarService
对象注入到属性中。 服务对象用于从数据库检索数据。
@RequestMapping("/")
public String index(Model model) {
return "Home page";
}
对于根路径,我们返回一个字符串消息。
@RequestMapping(name = "/cars", method = RequestMethod.GET)
public List<Car> cars() {
List<Car> cars = carService.findAll();
return cars;
}
在cars()
方法中,我们使用findAll()
方法找到所有汽车。 汽车对象列表通过 Spring 消息转换器转换为 JSON。
ICarService.java
package com.zetcode.service;
import com.zetcode.bean.Car;
import java.util.List;
public interface ICarService {
public List<Car> findAll();
}
ICarService
提供了一种从数据源获取所有汽车的契约方法。
CarService.java
package com.zetcode.service;
import com.zetcode.bean.Car;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class CarService implements ICarService {
@Autowired
private JdbcTemplate jtm;
@Override
public List<Car> findAll() {
String sql = "SELECT * FROM Cars";
List<Car> cars = jtm.query(sql, new BeanPropertyRowMapper(Car.class));
return cars;
}
}
CarService
包含findAll()
方法的实现。 我们借助JdbcTemplate
从Cars
表中检索所有汽车。
@Autowired
private JdbcTemplate jtm;
注入JdbcTemplate
。
String sql = "SELECT * FROM Cars";
这是要执行的 SQL。 我们从Cars
表中选择所有汽车。
List<Car> cars = jtm.query(sql, new BeanPropertyRowMapper(Car.class));
BeanPropertyRowMapper
将一行转换为指定映射目标类的新实例。
Application.java
package com.zetcode.client;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication(scanBasePackages = "com.zetcode")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。
$ mvn spring-boot:run
我们启动 Spring Boot 应用。
$ curl localhost:8080
Home page
当我们请求主页时,我们会收到字符串消息。
$ curl localhost:8080/cars
[{"id":1,"name":"Audi","price":52642},{"id":2,"name":"Mercedes","price":57127},
{"id":3,"name":"Skoda","price":9000},{"id":4,"name":"Volvo","price":29000},
{"id":5,"name":"Bentley","price":350000},{"id":6,"name":"Citroen","price":21000},
{"id":7,"name":"Hummer","price":41400},{"id":8,"name":"Volkswagen","price":21600}]
在这里,我们以 JSON 字符串获取汽车列表。
在本教程中,我们在 RESTFul Web 应用中使用了内存中的 H2 数据库。 该应用使用 Spring Boot 框架,并在具有嵌入式 Jetty 的 Web 环境中运行。 您可能也对这些相关教程感兴趣: Spring Boot DataSourceBuilder
教程, Spring Boot iText 教程, Spring Boot RESTFul 应用, Spring Web 应用简介, Spring Boot 第一个 Web 应用和 Java 教程。
Spring Boot CommandLineRunner
教程
Spring Boot CommandLineRunner
教程展示了如何使用CommandLineRunner
接口运行 bean。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。
CommandLineRunner
CommandLineRunner
是用于指示当 bean 包含在SpringApplication
中时应运行的接口。 一个 Spring Boot 应用可以有多个实现CommandLineRunner
的 bean。 可以通过@Order
规定。
Spring Boot CommandLineRunner
示例
以下应用演示了CommandLineRunner
的用法。 它在 H2 内存数据库中创建城市,然后列出它们。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ ├───model
│ │ │ City.java
│ │ └───repository
│ │ CityRepository.java
│ └───resources
│ application.properties
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootcommandlinerunner</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven pom.xml
文件。 我们使用 H2 数据库和 Spring Data JPA。
resources/application.properties
spring.main.banner-mode=off
application.properties
是 Spring Boot 中的主要配置文件。 使用spring.main.banner-mode=off
,我们关闭了 Spring 横幅。
com/zetcode/model/City.java
package com.zetcode.model;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "cities")
public class City {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int population;
public City() {
}
public City(String name, int population) {
this.name = name;
this.population = population;
}
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 int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.id);
hash = 79 * hash + Objects.hashCode(this.name);
hash = 79 * hash + this.population;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final City other = (City) obj;
if (this.population != other.population) {
return false;
}
if (!Objects.equals(this.name, other.name)) {
return false;
}
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append("City{id=").append(id).append(", name=")
.append(name).append(", population=")
.append(population).append("}");
return builder.toString();
}
}
这是City
模型,具有以下属性:id
,name
和population
。
com/zetcode/repository/CityRepository.java
package com.zetcode.repository;
import com.zetcode.model.City;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CityRepository extends CrudRepository<City, Long> {
}
CityRepository
在某个城市的存储库中具有一些通用的 CRUD 操作。
com/zetcode/MyRunner.java
package com.zetcode;
import com.zetcode.model.City;
import com.zetcode.repository.CityRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);
@Autowired
private CityRepository repository;
@Override
public void run(String... args) throws Exception {
repository.deleteAll();
repository.save(new City("Bratislava", 432000));
repository.save(new City("Budapest", 1759000));
repository.save(new City("Prague", 1280000));
repository.findAll().forEach((city) -> {
logger.info("{}", city);
});
}
}
通过实现CommandLineRunner
,将在应用启动后执行MyRunner
类的run()
方法。
@Component
public class MyRunner implements CommandLineRunner {
MyRunner
也装饰有@Component
,因此也会自动检测并注册。
@Autowired
private CityRepository repository;
使用@Autowired
注解,我们将CityRepository
bean 注入到repository
字段中。
@Override
public void run(String... args) throws Exception {
repository.save(new City("Bratislava", 432000));
repository.save(new City("Budapest", 1759000));
repository.save(new City("Prague", 1280000));
repository.findAll().forEach((city) -> {
logger.info("{}", city);
});
在run()
方法中,我们创建三个城市,然后找到所有城市并将其打印到控制台。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
是设置 Spring Boot 应用的入口。
我们使用mvn -q spring-boot:run
运行该应用。
在本教程中,我们展示了如何使用CommandLineRunner
接口创建在应用启动时运行的 bean。 您可能也对相关教程感兴趣: Spring Boot @Order
教程, Spring Boot @Repository
教程, Java 教程或列出了全部 Spring 入门教程。
Spring Boot Web JasperReports 集成
在本教程中,我们展示如何在 Spring Boot 框架中使用 JasperReports。 我们创建一个 Web 应用。
JasperReports 是一个 Java 开源报告库。 它可以创建各种格式的报告,包括 PDF,HTML,XLS 或 CSV。 JasperReports 以一种简单而灵活的方式创建了面向页面的可打印文档。
JdbcTemplate
是一个 Spring 库,可以帮助程序员创建与关系数据库和 JDBC 一起使用的应用。 它会处理许多繁琐且容易出错的底层细节,例如处理事务,清理资源以及正确处理异常。 JdbcTemplate
在 Spring 的spring-jdbc
模块中提供。
Spring 是用于开发 Java 企业应用的 Java 应用框架。 它还有助于集成各种企业组件。 Spring Boot 使创建具有 Spring 动力的生产级应用和服务变得很容易,而对安装的要求却最低。
Apache Derby 是完全用 Java 实现的开源关系数据库。 它占地面积小,易于部署和安装。 它可以在嵌入式和客户端/服务器模式下运行。
CARS
表
我们使用下表:
cars.sql
-- SQL for the CARS table
CREATE TABLE CARS(ID BIGINT NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY
(START WITH 1, INCREMENT BY 1), NAME VARCHAR(30), PRICE INT);
INSERT INTO CARS(Name, Price) VALUES('Audi', 52642);
INSERT INTO CARS(Name, Price) VALUES('Mercedes', 57127);
INSERT INTO CARS(Name, Price) VALUES('Skoda', 9000);
INSERT INTO CARS(Name, Price) VALUES('Volvo', 29000);
INSERT INTO CARS(Name, Price) VALUES('Bentley', 350000);
INSERT INTO CARS(Name, Price) VALUES('Citroen', 21000);
INSERT INTO CARS(Name, Price) VALUES('Hummer', 41400);
INSERT INTO CARS(Name, Price) VALUES('Volkswagen', 21600);
cars.sql
文件创建CARS
表。
$ $DERBY_HOME/bin/ij
ij version 10.11
ij> CONNECT 'jdbc:derby:testdb';
ij> RUN 'cars.sql';
一种选择是使用ij
工具从 SQL 脚本创建表。 请参考 Apache Derby 教程以熟悉 Derby。
$ $DERBY_HOME/bin/NetworkServerControl start &
使用NetworkServerControl
工具启动 Derby 服务器。
应用
以下 Spring Boot 应用从数据库表加载数据,并使用 JasperReports 库从中生成 PDF 报告。 该应用与嵌入式 Tomcat 服务器一起运行。
$ tree
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ ├── bean
│ │ │ └── Car.java
│ │ ├── conf
│ │ │ ├── AppConfig.java
│ │ │ └── MvcConf.java
│ │ ├── controller
│ │ │ └── MyController.java
│ │ └── service
│ │ ├── CarService.java
│ │ └── ICarService.java
│ └── resources
│ ├── application.yml
│ ├── report2.jrxml
│ └── static
│ └── index.html
└── test
└── java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>JasperSpringBootWeb</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derbyclient</artifactId>
<version>10.13.1.1</version>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Maven pom.xml
文件包含 JasperReports 库,Derby 驱动程序和 Spring Boot 的依赖项。
report2.xml
<?xml version = "1.0" encoding = "UTF-8"?>
<!DOCTYPE jasperReport PUBLIC "//JasperReports//DTD Report Design//EN"
"http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports
http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
name="report2" pageWidth="595" pageHeight="842"
columnWidth="555" leftMargin="20" rightMargin="20"
topMargin="20" bottomMargin="20">
<field name="Id" class="java.lang.Long">
<fieldDescription><![CDATA[id]]></fieldDescription>
</field>
<field name="Name" class="java.lang.String">
<fieldDescription><![CDATA[name]]></fieldDescription>
</field>
<field name="Price" class="java.lang.Integer">
<fieldDescription><![CDATA[price]]></fieldDescription>
</field>
<detail>
<band height="15">
<textField>
<reportElement x="0" y="0" width="50" height="15" />
<textElement textAlignment="Right" verticalAlignment="Middle"/>
<textFieldExpression class="java.lang.Long">
<![CDATA[$F{Id}]]>
</textFieldExpression>
</textField>
<textField>
<reportElement x="150" y="0" width="100" height="15" />
<textElement textAlignment="Left" verticalAlignment="Middle"/>
<textFieldExpression class="java.lang.String">
<![CDATA[$F{Name}]]>
</textFieldExpression>
</textField>
<textField>
<reportElement x="200" y="0" width="100" height="15"/>
<textElement textAlignment="Right" verticalAlignment="Middle"/>
<textFieldExpression class="java.lang.Integer">
<![CDATA[$F{Price}]]>
</textFieldExpression>
</textField>
</band>
</detail>
</jasperReport>
这是报告模板文件。 它位于src/main/resources
目录中。 模板仅包含明细带。 在详细信息区域内,将对数据源提供的每个记录重复每个元素。
<field name="Id" class="java.lang.Long">
<fieldDescription><![CDATA[id]]></fieldDescription>
</field>
<field name="Name" class="java.lang.String">
<fieldDescription><![CDATA[name]]></fieldDescription>
</field>
<field name="Price" class="java.lang.Integer">
<fieldDescription><![CDATA[price]]></fieldDescription>
</field>
报告中有三个字段。 这些字段映射到数据源 bean 的元素。
<textField>
<reportElement x="0" y="0" width="50" height="15" />
<textElement textAlignment="Right" verticalAlignment="Middle"/>
<textFieldExpression class="java.lang.Long">
<![CDATA[$F{Id}]]>
</textFieldExpression>
</textField>
文本字段是充满动态数据的元素。 我们将一个字段中的值放在文本字段中。 我们使用$F{}
语法引用该变量。
Car.java
package com.zetcode.bean;
public class Car {
private Long id;
private String name;
private int price;
public Car() {}
public Car(Long id, String name, int price) {
this.id = id;
this.name = name;
this.price = price;
}
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 int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" + "id=" + id + ", name=" + name + ", price=" + price + '}';
}
}
这是Car
bean 类。 它包含商品 ID,名称和价格。
application.yml
datasource:
url: jdbc:derby://localhost:1527/testdb
username: app
password: app
driverClassName: org.apache.derby.jdbc.ClientDriver
application.yml
是主要的 Spring Boot 配置文件。 它包含 Derby 数据源。
AppConfig.java
package com.zetcode.conf;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class AppConfig {
@Bean
@Primary
@ConfigurationProperties(prefix = "datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
}
AppConfig
是 Java 配置类。 它从配置文件创建数据源 bean。
MyController.java
package com.zetcode.controller;
import com.zetcode.service.ICarService;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView;
@Controller
public class MyController {
@Autowired
private ApplicationContext appContext;
@Autowired
private ICarService carService;
@RequestMapping(path = "/pdf", method = RequestMethod.GET)
public ModelAndView report() {
JasperReportsPdfView view = new JasperReportsPdfView();
view.setUrl("classpath:report2.jrxml");
view.setApplicationContext(appContext);
Map<String, Object> params = new HashMap<>();
params.put("datasource", carService.findAll());
return new ModelAndView(view, params);
}
}
在MyController
中,我们有两种方法可以响应两个请求。
@Autowired
private ICarService carService;
我们将CarService
对象注入到属性中。 服务对象用于从数据库检索数据。
@RequestMapping(path = "/pdf", method = RequestMethod.GET)
public ModelAndView report() {
JasperReportsPdfView view = new JasperReportsPdfView();
view.setUrl("classpath:report2.jrxml");
view.setApplicationContext(appContext);
Map<String, Object> params = new HashMap<>();
params.put("datasource", carService.getCars());
return new ModelAndView(view, params);
}
在report()
方法中,将生成报告并将其发送回客户端。 JasperReportsPdfView
是 Spring 类,可从提供的模板和数据生成 PDF 报告。
ICarService.java
package com.zetcode.service;
import com.zetcode.bean.Car;
import java.util.List;
public interface ICarService {
public List<Car> findAll();
}
ICarService
提供了一种从数据源获取所有汽车的契约方法。
CarService.java
package com.zetcode.service;
import com.zetcode.bean.Car;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class CarService implements ICarService {
@Autowired
private JdbcTemplate jtm;
@Override
public List<Car> findAll() {
String sql = "SELECT * FROM Cars";
List<Car> cars = jtm.query(sql, new BeanPropertyRowMapper(Car.class));
return cars;
}
}
CarService
包含findAll()
方法的实现。 我们借助JdbcTemplate
从CARS
表中检索所有汽车。
@Autowired
private JdbcTemplate jtm;
注入JdbcTemplate
。
String sql = "SELECT * FROM Cars";
这是要执行的 SQL。 我们从CARS
表中选择所有汽车。
List<Car> cars = jtm.query(sql, new BeanPropertyRowMapper(Car.class));
BeanPropertyRowMapper
将一行转换为指定映射目标类的新实例。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Home Page</title>
</head>
<body>
<a href="/pdf.html">Generate report</a>
</body>
</html>
index.html
文件包含一个生成 PDF 报告的链接。
Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。
在本教程中,我们使用 JasperReports 创建了 PDF 报告。 该应用使用 Spring Boot 框架,并在 Web 环境中运行。 您可能也对这些相关教程感兴趣: Spring Boot JasperReports CMD 集成,使用 JasperReports API 创建报告,使用 JasperReports 从 CSV 创建报告。 Java 教程。
Spring Boot iText 教程
在本教程中,我们将展示如何使用 iText 和 Spring Boot 创建 PDF 报告。 数据是从 H2 内存数据库中的表加载的。
iText 是一个开放源代码库,用于在 Java 中创建和处理 PDF 文件。
Spring 是用于开发 Java 企业应用的 Java 应用框架。 它还有助于集成各种企业组件。 Spring Boot 使创建具有 Spring 动力的生产级应用和服务变得很容易,而对安装的要求却最低。
H2 是完全用 Java 实现的开源关系数据库管理系统。 它可以嵌入 Java 应用中或以客户端-服务器模式运行。 它占地面积小,易于部署和安装。 它包含一个基于浏览器的控制台应用,用于查看和编辑数据库表。
JdbcTemplate
是一个 Spring 库,可以帮助程序员创建与关系数据库和 JDBC 一起使用的应用。 它会处理许多繁琐且容易出错的底层细节,例如处理事务,清理资源以及正确处理异常。 JdbcTemplate
在 Spring 的spring-jdbc
模块中提供。
应用
以下 Spring Boot 应用从数据库表中加载数据,并使用 iText 库从中生成 PDF 报告。 该应用与嵌入式 Tomcat 服务器一起运行。
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ ├── bean
│ │ │ └── Car.java
│ │ ├── conf
│ │ │ └── AppConfig.java
│ │ ├── controller
│ │ │ └── MyController.java
│ │ ├── service
│ │ │ ├── CarService.java
│ │ │ └── ICarService.java
│ │ └── view
│ │ ├── AbstractPdfView.java
│ │ └── MyPdfView.java
│ └── resources
│ ├── application.yml
│ ├── data-h2.sql
│ ├── schema-h2.sql
│ └── static
│ └── index.html
└── test
└── java
这是项目结构。
pom.xml
<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.zetcode</groupId>
<artifactId>SpringBootItext</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>4.2.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Maven pom.xml
文件包含 iText 库,H2 驱动程序和 Spring 框架的依赖项。
Car.java
package com.zetcode.bean;
public class Car {
private Long id;
private String name;
private int price;
public Car() {}
public Car(Long id, String name, int price) {
this.id = id;
this.name = name;
this.price = price;
}
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 int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" + "id=" + id + ", name=" + name + ", price=" + price + '}';
}
}
这是Car
bean 类。 它包含商品 ID,名称和价格。
application.yml
datasource:
url: jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
username: sa
password:
driverClassName: org.h2.Driver
spring:
datasource:
platform: h2
h2:
console:
enabled: true
path: /console/
application.yml
是主要的 Spring Boot 配置文件。 它包含数据源和 MVC 设置。 我们选择了 H2 作为数据库系统。 数据库在内存中运行。 我们启用基于浏览器的控制台应用。
url: jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
数据库名称为testdb
,并在内存中创建。 (当 Spring Boot 在 Maven POM 文件中发现 H2 时,它会自动创建一个内存数据库,但我们将展示如何显式地执行该操作。)
spring:
datasource:
platform: h2
该平台值用在 SQL 初始化脚本中:schema-${platform}.sql
和data-${platform}.sql
。
h2:
console:
enabled: true
path: /console/
H2 Web 控制台应用已启用; 在localhost:8080/console/
路径中可用。
schema-h2.sql
CREATE TABLE Cars(ID BIGINT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(30), PRICE INT);
该 SQL 脚本创建Cars
表。
data-h2.sql
INSERT INTO Cars(Name, Price) VALUES('Audi', 52642);
INSERT INTO Cars(Name, Price) VALUES('Mercedes', 57127);
INSERT INTO Cars(Name, Price) VALUES('Skoda', 9000);
INSERT INTO Cars(Name, Price) VALUES('Volvo', 29000);
INSERT INTO Cars(Name, Price) VALUES('Bentley', 350000);
INSERT INTO Cars(Name, Price) VALUES('Citroen', 21000);
INSERT INTO Cars(Name, Price) VALUES('Hummer', 41400);
INSERT INTO Cars(Name, Price) VALUES('Volkswagen', 21600);
该脚本用数据填充表。 这两个脚本都位于类路径的根目录中。
AppConfig.java
package com.zetcode.conf;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class AppConfig {
@Bean
@Primary
@ConfigurationProperties(prefix = "datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
}
AppConfig
是 Java 配置类。 它从application.yml
配置文件创建数据源 bean。
AbstractPdfView.java
package com.zetcode.view;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.ByteArrayOutputStream;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.view.AbstractView;
public abstract class AbstractPdfView extends AbstractView {
public AbstractPdfView() {
initView();
}
private void initView() {
setContentType("application/pdf");
}
@Override
protected boolean generatesDownloadContent() {
return true;
}
@Override
protected final void renderMergedOutputModel(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response) throws Exception {
ByteArrayOutputStream baos = createTemporaryOutputStream();
Document document = new Document(PageSize.A4);
PdfWriter writer = PdfWriter.getInstance(document, baos);
prepareWriter(model, writer, request);
buildPdfMetadata(model, document, request);
document.open();
buildPdfDocument(model, document, writer, request, response);
document.close();
writeToResponse(response, baos);
}
protected void prepareWriter(Map<String, Object> model, PdfWriter writer,
HttpServletRequest request) throws DocumentException {
writer.setViewerPreferences(getViewerPreferences());
}
protected int getViewerPreferences() {
return PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage;
}
protected void buildPdfMetadata(Map<String, Object> model, Document document,
HttpServletRequest request) {
}
protected abstract void buildPdfDocument(Map<String, Object> model,
Document document, PdfWriter writer, HttpServletRequest request,
HttpServletResponse response) throws Exception;
}
Spring 的AbstractPdfView
基于旧的iText
库。 因此,我们需要创建自己的抽象类,该抽象类基本上是具有更新的导入的副本。
MyPdfView.java
package com.zetcode.view;
import com.itextpdf.text.Document;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.FontFactory;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.zetcode.bean.Car;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyPdfView extends AbstractPdfView {
@Override
protected void buildPdfDocument(Map<String, Object> model,
Document document, PdfWriter writer, HttpServletRequest request,
HttpServletResponse response) throws Exception {
List<Car> cars = (List<Car>) model.get("cars");
PdfPTable table = new PdfPTable(3);
table.setWidthPercentage(60);
table.setWidths(new int[] {1, 3, 3});
Font headFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD);
PdfPCell hcell;
hcell = new PdfPCell(new Phrase("Id", headFont));
hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(hcell);
hcell = new PdfPCell(new Phrase("Name", headFont));
hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(hcell);
hcell = new PdfPCell(new Phrase("Price", headFont));
hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(hcell);
for (Car car : cars) {
PdfPCell cell;
cell = new PdfPCell(new Phrase(car.getId().toString()));
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(cell);
cell = new PdfPCell(new Phrase(car.getName()));
cell.setPaddingLeft(5);
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.setHorizontalAlignment(Element.ALIGN_LEFT);
table.addCell(cell);
cell = new PdfPCell(new Phrase(String.valueOf(car.getPrice())));
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
cell.setPaddingRight(5);
table.addCell(cell);
}
document.add(table);
}
}
MyPdfView
继承自定制的AbstractPdfView
。 在buildPdfDocument()
方法中,我们生成 PDF 文件。
List<Car> cars = (List<Car>) model.get("cars");
首先,我们从模型中获取数据。
PdfPTable table = new PdfPTable(3);
我们将数据放在表格中; 为此,我们有PdfPTable
类。 该表包含三列:ID,名称和价格。
Font headFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD);
对于表头,我们使用粗体的 Helvetica 字体。
PdfPCell hcell;
hcell = new PdfPCell(new Phrase("Id", headFont));
hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(hcell);
数据放置在表单元格内,由PdfPCell
表示。 使用setHorizontalAlignment()
方法将文本水平对齐。
document.add(table);
最后,表格被插入到 PDF 文档中。
MyController.java
package com.zetcode.controller;
import com.zetcode.bean.Car;
import com.zetcode.service.ICarService;
import com.zetcode.view.MyPdfView;
import java.util.HashMap;
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;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class MyController {
@Autowired
private ICarService carService;
@RequestMapping(path = "/report", method = RequestMethod.GET)
public ModelAndView report() {
Map<String, Object> model = new HashMap<>();
List<Car> cars = carService.findAll();
model.put("cars", cars);
return new ModelAndView(new MyPdfView(), model);
}
}
在MyController
中,我们有一个映射。
@Autowired
private ICarService carService;
我们将CarService
对象注入到属性中。 服务对象用于从数据库检索数据。
@RequestMapping(path = "/report", method = RequestMethod.GET)
public ModelAndView report() {
Map<String, Object> model = new HashMap<>();
List<Car> cars = carService.findAll();
model.put("cars", cars);
return new ModelAndView(new MyPdfView(), model);
}
在report()
方法中,我们使用findAll()
方法找到所有汽车。 我们将自定义MyPdfView
返回给客户端。
ICarService.java
package com.zetcode.service;
import com.zetcode.bean.Car;
import java.util.List;
public interface ICarService {
public List<Car> findAll();
}
ICarService
提供了一种从数据源获取所有汽车的契约方法。
CarService.java
package com.zetcode.service;
import com.zetcode.bean.Car;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class CarService implements ICarService {
@Autowired
private JdbcTemplate jtm;
@Override
public List<Car> findAll() {
String sql = "SELECT * FROM Cars";
List<Car> cars = jtm.query(sql, new BeanPropertyRowMapper(Car.class));
return cars;
}
}
CarService
包含findAll()
方法的实现。 我们借助JdbcTemplate
从Cars
表中检索所有汽车。
@Autowired
private JdbcTemplate jtm;
注入JdbcTemplate
。
String sql = "SELECT * FROM Cars";
这是要执行的 SQL。 我们从Cars
表中选择所有汽车。
List<Car> cars = jtm.query(sql, new BeanPropertyRowMapper(Car.class));
BeanPropertyRowMapper
将一行转换为指定映射目标类的新实例。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Home Page</title>
</head>
<body>
<a href="/report.html">Generate report</a>
</body>
</html>
index.html
文件包含一个生成 PDF 报告的链接。 静态文件从预定义目录提供; 其中之一是static
,位于src/main/resources
中。
Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。
$ mvn spring-boot:run
我们启动 Spring Boot 应用。
导航至http://localhost:8080/
以测试应用。 H2 控制台应用可从http://localhost:8080/console/
获得。 控制台应用的 JDBC URL 为jdbc:h2:mem:testdb
。 密码为空。
在本教程中,我们从 H2 数据库的数据库表中使用 iText 创建了 PDF 报告。 该应用使用 Spring Boot 框架,并在 Web 环境中运行。 您可能也对这些相关教程感兴趣: Spring Boot H2 教程, Spring Boot JasperReports Web 集成, Spring Web 应用简介, Spring Boot 第一个 Web 应用和 Java 教程。
Spring Boot CMD JasperReports 集成
在本教程中,我们展示如何在 Spring Boot 框架中使用 JasperReports。 Spring Boot 在命令行中运行。
JasperReports 是一个 Java 开源报告库。 它可以创建各种格式的报告,包括 PDF,HTML,XLS 或 CSV。 JasperReports 以一种简单而灵活的方式创建了面向页面的可打印文档。
JdbcTemplate 是一个 Spring 库,可以帮助程序员创建与关系数据库和 JDBC 一起使用的应用。 它会处理许多繁琐且容易出错的底层细节,例如处理事务,清理资源以及正确处理异常。 JdbcTemplate
在 Spring 的spring-jdbc
模块中提供。
Spring 是用于开发 Java 企业应用的流行 Java 应用框架。 它还有助于集成各种企业组件。 Spring Boot 使创建具有 Spring 动力的生产级应用和服务变得很容易,而对安装的要求却最低。
Apache Derby 是完全用 Java 实现的开源关系数据库。 它占地面积小,易于部署和安装。 它可以在嵌入式和客户端/服务器模式下运行。
CARS
表
在我们的应用中,我们使用下表:
cars.sql
-- SQL for the CARS table
CREATE TABLE CARS(ID BIGINT NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY
(START WITH 1, INCREMENT BY 1), NAME VARCHAR(30), PRICE INT);
INSERT INTO CARS(Name, Price) VALUES('Audi', 52642);
INSERT INTO CARS(Name, Price) VALUES('Mercedes', 57127);
INSERT INTO CARS(Name, Price) VALUES('Skoda', 9000);
INSERT INTO CARS(Name, Price) VALUES('Volvo', 29000);
INSERT INTO CARS(Name, Price) VALUES('Bentley', 350000);
INSERT INTO CARS(Name, Price) VALUES('Citroen', 21000);
INSERT INTO CARS(Name, Price) VALUES('Hummer', 41400);
INSERT INTO CARS(Name, Price) VALUES('Volkswagen', 21600);
cars.sql
文件创建CARS
表。
$ $DERBY_HOME/bin/ij
ij version 10.11
ij> CONNECT 'jdbc:derby:testdb';
ij> RUN 'cars.sql';
一种选择是使用ij
工具从 SQL 脚本创建表。 请参考 Apache Derby 教程以熟悉 Derby。
$ $DERBY_HOME/bin/NetworkServerControl start &
使用NetworkServerControl
工具启动 Derby 服务器。
应用
以下 Spring Boot 应用从数据库表加载数据,并使用 JasperReports 库从中生成 PDF 报告。
$ tree
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ ├── bean
│ │ │ └── Car.java
│ │ ├── conf
│ │ │ └── AppConfig.java
│ │ ├── MyRunner.java
│ │ ├── report
│ │ │ └── ReportGenerator.java
│ │ └── service
│ │ ├── CarService.java
│ │ └── ICarService.java
│ └── resources
│ ├── application.yml
│ └── report2.xml
└── test
└── java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>JasperSpringBootCmd</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derbyclient</artifactId>
<version>10.13.1.1</version>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<name>JasperSpringBootCmd</name>
</project>
Maven pom.xml
文件包含以下依赖项:derbyclient
,jasperreports
和spring-boot-starter-jdbc
。 jasperreports
依赖项是 JasperReports 库; spring-boot-starter-jdbc
包含JdbcTemplate
库。
report2.xml
<?xml version = "1.0" encoding = "UTF-8"?>
<!DOCTYPE jasperReport PUBLIC "//JasperReports//DTD Report Design//EN"
"http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports
http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
name="report2" pageWidth="595" pageHeight="842"
columnWidth="555" leftMargin="20" rightMargin="20"
topMargin="20" bottomMargin="20">
<field name="Id" class="java.lang.Long">
<fieldDescription><![CDATA[id]]></fieldDescription>
</field>
<field name="Name" class="java.lang.String">
<fieldDescription><![CDATA[name]]></fieldDescription>
</field>
<field name="Price" class="java.lang.Integer">
<fieldDescription><![CDATA[price]]></fieldDescription>
</field>
<detail>
<band height="15">
<textField>
<reportElement x="0" y="0" width="50" height="15" />
<textElement textAlignment="Right" verticalAlignment="Middle"/>
<textFieldExpression class="java.lang.Long">
<![CDATA[$F{Id}]]>
</textFieldExpression>
</textField>
<textField>
<reportElement x="150" y="0" width="100" height="15" />
<textElement textAlignment="Left" verticalAlignment="Middle"/>
<textFieldExpression class="java.lang.String">
<![CDATA[$F{Name}]]>
</textFieldExpression>
</textField>
<textField>
<reportElement x="200" y="0" width="100" height="15"/>
<textElement textAlignment="Right" verticalAlignment="Middle"/>
<textFieldExpression class="java.lang.Integer">
<![CDATA[$F{Price}]]>
</textFieldExpression>
</textField>
</band>
</detail>
</jasperReport>
这是报告模板文件。 模板仅包含明细带。 在详细信息区域内,将对数据源提供的每个记录重复每个元素。
<field name="Id" class="java.lang.Long">
<fieldDescription><![CDATA[id]]></fieldDescription>
</field>
<field name="Name" class="java.lang.String">
<fieldDescription><![CDATA[name]]></fieldDescription>
</field>
<field name="Price" class="java.lang.Integer">
<fieldDescription><![CDATA[price]]></fieldDescription>
</field>
报告中有三个字段。 这些字段映射到数据源 bean 的元素。
<textField>
<reportElement x="0" y="0" width="50" height="15" />
<textElement textAlignment="Right" verticalAlignment="Middle"/>
<textFieldExpression class="java.lang.Long">
<![CDATA[$F{Id}]]>
</textFieldExpression>
</textField>
文本字段是充满动态数据的元素。 我们将一个字段中的值放在文本字段中。 我们使用$F{}
语法引用该变量。
Car.java
package com.zetcode.bean;
public class Car {
private Long id;
private String name;
private int price;
public Car() {}
public Car(Long id, String name, int price) {
this.id = id;
this.name = name;
this.price = price;
}
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 int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" + "id=" + id + ", name=" + name + ", price=" + price + '}';
}
}
这是Car
bean 类。 它包含商品 ID,名称和价格。
application.yml
datasource:
url: jdbc:derby://localhost:1527/testdb
username: app
password: app
driverClassName: org.apache.derby.jdbc.ClientDriver
application.yml
是主要的 Spring Boot 配置文件。 它包含 Derby 数据源设置。
AppConfig.java
package com.zetcode.conf;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class AppConfig {
@Bean
@Primary
@ConfigurationProperties(prefix = "datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
}
AppConfig
是 Java 配置类。 它从配置文件创建数据源 bean。
ReportGenerator.java
package com.zetcode.report;
import com.zetcode.bean.Car;
import java.util.HashMap;
import java.util.List;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
public class ReportGenerator {
public void generatePdfReport(List<Car> cars) throws JRException {
String report = "src/main/resources/report2.xml";
JasperReport jreport = JasperCompileManager.compileReport(report);
JRBeanCollectionDataSource ds = new JRBeanCollectionDataSource(cars);
HashMap params = new HashMap();
JasperPrint jprint = JasperFillManager.fillReport(jreport, params, ds);
JasperExportManager.exportReportToPdfFile(jprint,
"src/main/resources/report2.pdf");
}
}
ReportGenerator
根据提供的数据创建 PDF 报告。
JasperReport jreport = JasperCompileManager.compileReport(report);
使用JasperCompileManager.compileReport()
方法,我们将 XML 报告模板编译为中介JasperReport
。
JRBeanCollectionDataSource ds = new JRBeanCollectionDataSource(cars);
从提供的汽车对象列表中,我们创建一个JRBeanCollectionDataSource
。 Bean 的属性将被映射到已编译报表对象的字段。
JasperPrint jprint = JasperFillManager.fillReport(jreport, params, ds);
已编译的 Jasper 对象使用JasperFillManager.fillReport()
方法填充了数据。 产生一个JasperPrint
对象。
JasperExportManager.exportReportToPdfFile(jprint,
"src/main/resources/report2.pdf");
JasperExportManager.exportReportToPdfFile()
方法将JasperPrint
对象转换为 PDF 文件。
ICarService.java
package com.zetcode.service;
import com.zetcode.bean.Car;
import java.util.List;
public interface ICarService {
public List<Car> findAll();
}
ICarService
提供了一种从数据源获取所有汽车的契约方法。
CarService.java
package com.zetcode.service;
import com.zetcode.bean.Car;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class CarService implements ICarService {
@Autowired
private JdbcTemplate jtm;
@Override
public List<Car> findAll() {
String sql = "SELECT * FROM Cars";
List<Car> cars = jtm.query(sql, new BeanPropertyRowMapper(Car.class));
return cars;
}
}
CarService
包含findAll()
方法的实现。 我们借助JdbcTemplate
从CARS
表中检索所有汽车。
MyRunner.java
package com.zetcode;
import com.zetcode.bean.Car;
import com.zetcode.report.ReportGenerator;
import com.zetcode.service.ICarService;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
private static final Logger LOG = Logger.getLogger(MyRunner.class.getName());
@Autowired
private ICarService carService;
@Override
public void run(String... args) throws Exception {
List<Car> cars = carService.findAll();
ReportGenerator rg = new ReportGenerator();
rg.generatePdfReport(cars);
LOG.log(Level.INFO, "Generating PDF report");
}
}
MyRunner
是 Spring Boot 应用的命令行运行程序。
@Autowired
private ICarService carService;
我们注入服务对象。
List<Car> cars = carService.findAll();
我们使用服务对象获取所有汽车。
ReportGenerator rg = new ReportGenerator();
rg.generatePdfReport(cars);
ReportGenerator
用于创建 PDF 报告。 该报告包含检索到的汽车。
Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。
在本教程中,我们使用 JasperReports 创建了 PDF 报告。 该应用使用了 Spring Boot 框架,并在命令行中运行。 您可能也对这些相关教程感兴趣: Spring Boot JasperReports Web 集成,使用 JasperReports API 创建报告,使用 JasperReports 从 CSV 创建报告和 Java 教程。
Spring Boot RESTFul 应用
在本教程中,我们将创建一个简单的 Spring Boot RESTful 应用。 我们的应用将部署在嵌入式 Tomcat 服务器上。
我们展示了如何从 Web 服务中以 JSON 和 XML 格式返回数据。
Spring Boot
Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是一种以最少的精力创建独立的,基于生产级别的基于 Spring 的应用的方法。
RESTFul 应用
RESTFul 应用创建遵循 REST 架构样式的系统(API),该系统用于设计联网应用。 RESTful 应用使用 HTTP 请求对资源执行 CRUD(创建/读取/更新/删除)操作。
Spring Boot RESTFul 简单示例
以下代码示例创建一个 Web 服务,该服务从 CSV 文件读取数据并将其以 JSON 格式返回给客户端。
$ tree
.
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ ├── bean
│ │ │ └── Country.java
│ │ ├── controller
│ │ │ └── MyController.java
│ │ └── service
│ │ ├── CountryService.java
│ │ └── ICountryService.java
│ └── resources
│ ├── application.yml
│ └── countries.csv
└── test
└── java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>SpringBootRest</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringBootRest</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>3.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 opencsv
用于处理 CSV 数据。 spring-boot-starter-web
是用于构建 Web 和 RESTful 应用的入门工具。 该应用打包到可执行的 JAR 文件中。 可执行 JAR 是使用spring-boot-maven-plugin
创建的。
application.yml
server:
port: 8086
contextPath: /rest
application.yml
文件包含 Spring Boot 应用的各种配置设置。 我们具有服务器端口和上下文路径(应用名称)的映射。 该文件位于src/main/resources
目录中。
countries.csv
Country, Population
Slovakia,5429000
Norway,5271000
Croatia,4225000
Russia,143439000
Mexico,122273000
Vietnam,95261000
Sweden,9967000
Iceland,337600
Israel,8622000
Hungary,9830000
Germany,82175700
Japan,126650000
src/main/resources
目录中的countries.csv
包含我们的应用中使用的数据。
Country.java
package com.zetcode.bean;
public class Country {
private String name;
private int population;
public Country() {
}
public Country(String name, int population) {
this.name = name;
this.population = population;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
}
countries.csv
文件中的字段映射到Country
类。
ICountryService.java
package com.zetcode.service;
import com.zetcode.bean.Country;
import java.util.ArrayList;
public interface ICountryService {
public ArrayList<Country> findAll();
}
这是ICountryService
接口。 它包含一种称为findAll()
的方法。
CountryService.java
package com.zetcode.service;
import com.opencsv.CSVReader;
import com.zetcode.bean.Country;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.stereotype.Service;
@Service
public class CountryService implements ICountryService {
private final ArrayList<Country> countries;
public CountryService() {
countries = new ArrayList();
}
@Override
public ArrayList<Country> findAll() {
FileInputStream fis = null;
try {
String fileName = "src/main/resources/countries.csv";
fis = new FileInputStream(new File(fileName));
CSVReader reader = new CSVReader(new InputStreamReader(fis));
String[] nextLine;
reader.readNext();
while ((nextLine = reader.readNext()) != null) {
Country newCountry = new Country(nextLine[0],
Integer.valueOf(nextLine[1]));
countries.add(newCountry);
}
} catch (FileNotFoundException ex) {
Logger.getLogger(CountryService.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(CountryService.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException ex) {
Logger.getLogger(CountryService.class.getName()).log(Level.SEVERE, null, ex);
}
}
return countries;
}
}
这是ICountryService
契约的执行。 它包含findAll()
方法,该方法从countries.csv
文件中读取数据并返回Country
对象的列表。
MyController.java
package com.zetcode.controller;
import com.zetcode.bean.Country;
import com.zetcode.service.ICountryService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@Autowired
private ICountryService countryService;
@RequestMapping("/countries")
public List<Country> listCountries() {
return countryService.findAll();
}
}
这是 Spring Boot RESTful 应用的控制器类。 @RestController
注解创建一个 RESTful 控制器。 传统的 MVC 控制器使用ModelAndView
,而 RESTful 控制器仅返回对象,并且对象数据以 JSON 或 XML 格式直接写入 HTTP 响应。
@Autowired
private ICountryService countryService;
我们将CountryService
注入countryService
变量中。
@RequestMapping("/countries")
public List<Country> listCountries() {
return countryService.findAll();
}
@RequestMapping
注解用于将 Web 请求映射到 Spring 控制器方法。 在这里,我们将具有/countries
路径的请求映射到控制器的listCountries()
方法。 默认请求是 GET 请求。
我们不需要手动将Country
域对象转换为 JSON。 因为 Jackson 2 在类路径上,所以 Spring 自动选择MappingJackson2HttpMessageConverter
将Country
实例转换为 JSON。
Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。 @SpringBootApplication
启用自动配置和组件扫描。
$ mvn package
使用mvn package
命令,构建应用。
$ mvn spring-boot:run
使用mvn spring-boot:run
命令,运行应用。 该应用部署在嵌入式 Tomcat 服务器上。
$ curl localhost:8086/rest/countries
[{"name":"Slovakia","population":5429000},{"name":"Norway","population":5271000},
{"name":"Croatia","population":4225000},{"name":"Russia","population":143439000},
{"name":"Mexico","population":122273000},{"name":"Vietnam","population":95261000},
{"name":"Sweden","population":9967000},{"name":"Iceland","population":337600},
{"name":"Israel","population":8622000},{"name":"Hungary","population":9830000},
{"name":"Germany","population":82175700},{"name":"Japan","population":126650000}]
使用curl
命令,测试应用。
返回 XML 数据
要返回 XML 数据而不是 JSON,我们需要添加一个依赖项并修改控制器。
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
我们将jackson-dataformat-xml
添加到依赖项。
MyController.java
package com.zetcode.controller;
import com.zetcode.bean.Country;
import com.zetcode.service.ICountryService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@Autowired
private ICountryService countryService;
@RequestMapping(value="/countries", method=RequestMethod.GET,
produces=MediaType.APPLICATION_XML_VALUE)
public List<Country> listCountries() {
return countryService.findAll();
}
}
我们选择MediaType.APPLICATION_XML_VALUE
类型来告诉控制器返回 XML 数据。
在本教程中,我们创建了一个 Spring Boot RESTful 应用,该应用以 JSON 和 XML 返回数据。 您可能也对相关教程感兴趣: Spring Boot H2 REST 教程, Spring Web 应用简介,独立的 Spring 应用, OpenCSV 教程 ,在经典的 Spring 应用中使用 HikariCP 连接池,JdbcTemplate
。
Spring Boot 第一个 Web 应用
Spring Boot 的第一个 Web 应用教程展示了如何创建一个简单的 Spring Boot Web 应用。 当前的趋势是从可执行的 JAR 启动 Spring Boot 应用。 (有关传统 WAR 部署的示例,请参见SpringBootServletInitializer
教程。)
Spring 是流行的 Java 应用框架。 Spring Boot 致力于以最小的努力创建独立的,基于生产级别的基于 Spring 的应用。
Spring Boot Web 应用示例
该应用显示一条消息和今天的日期。 该消息是从应用的属性中检索的。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ └───controller
│ │ MyController.java
│ └───resources
│ │ application.properties
│ └───templates
│ index.html
└───test
└───java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootfirstweb</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 spring-boot-starter-web
是使用 Spring MVC 构建 Web(包括 RESTful)应用的入门程序。
spring-boot-starter-thymeleaf
包含 Thymeleaf 模板引擎。 当 Spring Boot 检测到该启动程序时,它将自动为我们配置 Thymeleaf。
该应用打包到一个 JAR 文件中,该文件包含一个嵌入式 Tomcat Web 服务器。
resources/application.properties
application.message: Hello there
application.properties
文件包含 Spring Boot 应用的各种配置设置。 我们有一个自定义消息选项。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import java.time.LocalDate;
import java.util.Map;
@Controller
public class MyController {
@Value("${application.message}")
private String message = "Hi there";
@GetMapping("/")
public String index(Map<String, Object> model) {
model.put("now", LocalDate.now());
model.put("message", this.message);
return "index";
}
}
这是 Spring Boot Web 应用的控制器类。 控制器被饰以@Controller
注解。 控制器具有一个映射。 映射解析为index.jsp
,它位于WEB-INF/jsp
目录中。
@Value("${application.message}")
private String message = "Hi there";
我们将application.properties
中的值注入message
变量中。
@GetMapping("/")
public String index(Map<String, Object> model) {
model.put("now", LocalDate.now());
model.put("message", this.message);
return "index";
}
@GetMapping
注解将带有/路径的 GET 请求映射到索引方法处理器。 创建一个模型并填充数据。 该模型是Map
接口,可以完全抽象视图技术。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用。
resources/templates/index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Home page</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<p th:text="'Date: ' + ${now}"></p>
<p th:text="'Message: ' + ${message}"></p>
</body>
</html>
index.html
显示两个值:当前日期和收到的消息。 这两个值都通过控制器传递到模板。
$ mvn spring-boot:run
我们运行该应用。 现在我们可以导航到localhost:8080
以查看应用消息。
在本教程中,我们创建了第一个 Spring Boot Web 应用。 您可能也对相关教程感兴趣: Spring Web 应用简介,独立的 Spring 应用, FreeMarker 教程, Java 教程, 或列出所有 Spring Boot 教程。
Spring Boot Groovy CLI
Spring Boot Groovy CLI 教程是使用命令行界面的 Spring Boot 框架入门教程。 在本教程中,我们使用 Groovy 语言。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。 Spring 支持 Java 和 Groovy 语言。 Spring 5 支持 Kotlin 语言。
Spring Boot CLI
Spring Boot 命令行界面(CLI)是使用命令行工具创建 Spring Boot 应用的一种方式。 Spring Boot CLI 帮助开发者轻松创建和运行 Spring 应用。
请注意,Spring Boot CLI 可用于创建 Java 和 Groovy 应用。
安装 Spring Boot CLI
Spring Boot CLI 的主要命令是 Bash 脚本,它是 Spring Boot 加载程序的接口。
$ wget http://repo.spring.io/release/org/springframework/boot/spring-boot-cli/1.5.7.RELEASE/spring-boot-cli-1.5.7.RELEASE-bin.zip
我们从 Spring 数据库下载带有wget
的 CLI 工具。
$ unzip spring-boot-cli-1.5.7.RELEASE-bin.zip
我们将 ZIP 存档解压缩。
$ cd spring-1.5.7.RELEASE/
$ ls
bin INSTALL.txt legal lib LICENCE.txt shell-completion
在bin
目录中,有spring
工具是创建 Spring Boot 应用的主要命令。
$ export PATH=$PATH:~/bin/spring-1.5.7.RELEASE/bin/
我们可以将工具的主目录添加到PATH
变量中,以方便地访问命令。
Spring Boot Groovy 应用
Spring Boot Groovy 应用可用于快速应用开发和 Spring 应用原型设计。 Spring 尝试使用 Groovy 简化编程。 对于 Groovy 应用,Spring 在幕后做了很多魔术:
- 自动下载依赖项
- 自动配置应用
- 自动创建应用入口点
- 默认导入语句
使用spring run
命令启动 Groovy 应用。
简单的 Groovy 应用
以下是一个简单的 Spring Boot Groovy 应用。
first.groovy
@RestController
class MyApp {
@RequestMapping("/")
String home() {
"This is Spring Boot application"
}
}
该应用将文本消息返回给客户端。
@RestController
@RestController
指示一个 Restful Web 应用。 返回的字符串不表示模板文件的名称。 它直接返回给客户端。
@RequestMapping("/")
使用@RequestMapping
注解,我们将该方法映射到指定的 URL 路径。
$ spring run first.groovy
$ curl localhost:8080
This is Spring Boot application
我们使用spring run
命令运行该应用,并使用curl
测试它。 我们没有创建 Maven 或 Gradle 构建文件,也没有进行任何配置。 一切都由 Spring 在后台自动完成。
Freemarker 模板
在下面的示例中,我们使用 Freemarker 模板引擎创建一个简单的 Groovy Web 应用。
$ tree
.
├── app.groovy
├── static
│ └── index.html
└── templates
└── hello.ftl
我们有这个项目结构。 静态文件位于static
目录中,而模板文件位于templates
目录中。
index.html
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8">
</head>
<body>
<form action="/greet">
<label>Enter your name:</label>
<input type="text" name="name">
<button type="submit">Submit</button>
</form>
</body>
</html>
这是主页。 它包含一个将数据发送到 Web 应用的表单。
app.groovy
@Controller
@Grab('spring-boot-starter-freemarker')
class MyApp {
@RequestMapping("/greet")
String home(Model model, @RequestParam String name) {
model.addAttribute("myname", name)
return "hello"
}
}
这是主要的 Groovy 应用文件。
@Controller
@Controller
注解创建一个经典的 MVC Web 应用。 从方法返回的字符串是要处理的模板文件的名称。
@Grab('spring-boot-starter-freemarker')
由于 Spring 无法推断出我们正在使用 Freemarker,因此我们必须告诉 Spring 将其与@Grab
一起使用。 然后,Spring 为我们自动配置 Freemarker。 Freemarker 模板位于templates
目录中。
@RequestMapping("/greet")
String home(Model model, @RequestParam String name) {
model.addAttribute("myname", name)
return "hello"
}
home()
方法与@RequestMapping
注解映射到/greet
路径。 使用@RequestParam
,我们检索客户端发送的请求参数。 我们将参数添加到模型,并将处理转发到hello.ftl
模板文件。
hello.ftl
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8">
</head>
<body>
<p>Hello ${myname}, today is a beautiful day!</p>
</body>
</html>
hello.ftl
模板文件发送带有消息的 HTML 页面。 该消息包含从 HTML 表单发送的人员的姓名。 来自模型的值以 Freemarker ${}
语法显示。
在本教程中,我们使用 Spring Boot CLI 在 Groovy 中创建了简单的 Spring 应用。
您可能也对相关教程感兴趣: Spring Boot @RequestParam
教程, Spring Boot @ResponseBody
教程, Spring Boot REST H2 教程, Freemarker 教程, Java 教程。
Spring Boot 上传文件
Spring Boot 上传文件教程展示了如何使用 Spring Boot 框架上传单个文件。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。
HTML 表单的编码类型
POST 请求有三种编码 HTML 表单类型:
application/x-www-form-urlencoded
multipart/form-data
text/plain
application/x-www-form-urlencoded
是默认编码,其中值编码在由&
分隔的键值元组中。 =
字符用于键和值之间。 非字母数字字符采用百分比编码。 此编码类型不适用于二进制文件。
multipart/form-data
用于非 acsii 数据和二进制文件。 input
元素的type
属性设置为file
。
text/plain
用于调试。
Spring 上传文件示例
在下面的示例中,我们有一个 Web 表单来选择要上传到服务器的文件。 该文件被上传到/var/www/upload
目录。
上传目录
/var/www
目录是 Debian Linux 中 Web 内容的标准目录。
$ ls -ld /var/www/upload/
drwxrwxr-x 2 www-data www-data 4096 Dec 3 14:29 /var/www/upload/
我们将文件上传到/var/www/upload
目录。 www-data
组中的用户可以修改目录文件。 因此,运行 Web 服务器的用户必须在此组中。
应用
以下是 Spring Boot Web 应用的来源。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ ├───controller
│ │ │ MyController.java
│ │ ├───exception
│ │ │ StorageException.java
│ │ └───service
│ │ StorageService.java
│ └───resources
│ │ application.properties
│ └───static
│ failure.html
│ index.html
│ success.html
└───test
└───java
这是 Spring 应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootuploadfile</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven pom.xml
文件。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import com.zetcode.exception.StorageException;
import com.zetcode.service.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
@Controller
public class MyController {
@Autowired
private StorageService storageService;
@RequestMapping(value = "/doUpload", method = RequestMethod.POST,
consumes = {"multipart/form-data"})
public String upload(@RequestParam MultipartFile file) {
storageService.uploadFile(file);
return "redirect:/success.html";
}
@ExceptionHandler(StorageException.class)
public String handleStorageFileNotFound(StorageException e) {
return "redirect:/failure.html";
}
}
MyController
从请求中读取文件并将其保存到所选目录中。
@Autowired
private StorageService storageService;
StoreageService
将文件存储在磁盘上。
@RequestMapping(value = "/doUpload", method = RequestMethod.POST,
consumes = {"multipart/form-data"})
public String upload(@RequestParam MultipartFile file) {
upload()
方法映射到doUpload
URL 模式。 由于我们正在将数据发送到服务器,因此我们使用 POST 请求。 请求参数具有MultipartFile
类型。
return "redirect:/success.html";
成功上传文件后,我们会显示一条消息。
@ExceptionHandler(StorageException.class)
public String handleStorageFileNotFound(StorageException e) {
return "redirect:/failure.html";
}
我们有StorageException
的处理器。
com/zetcode/StorageException.java
package com.zetcode.exception;
public class StorageException extends RuntimeException {
public StorageException(String message) {
super(message);
}
public StorageException(String message, Throwable cause) {
super(message, cause);
}
}
这是我们的自定义StorageException
。 当文件无法存储在文件系统上时,将引发该错误。
com/zetcode/service/StorageService.java
package com.zetcode.service;
import com.zetcode.exception.StorageException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
@Service
public class StorageService {
@Value("${upload.path}")
private String path;
public void uploadFile(MultipartFile file) {
if (file.isEmpty()) {
throw new StorageException("Failed to store empty file");
}
try {
var fileName = file.getOriginalFilename();
var is = file.getInputStream();
Files.copy(is, Paths.get(path + fileName),
StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
var msg = String.format("Failed to store file", file.getName());
throw new StorageException(msg, e);
}
}
}
StorageService
从输入流复制数据并将其保存在磁盘上。
@Value("${upload.path}")
private String path;
我们使用@Value
注解从application.properties
文件中读取上传目录。
if (file.isEmpty()) {
throw new StorageException("Failed to store empty file");
}
我们确保已使用isEmpty()
方法选择了一个文件。
var fileName = file.getOriginalFilename();
我们使用getOriginalFilename()
方法获得文件名。
var is = file.getInputStream();
我们使用getInputStream()
方法获得输入流。
Files.copy(is, Paths.get(path + fileName),
StandardCopyOption.REPLACE_EXISTING);
该文件被复制到从与Files.copy()
输入流源的目标目录。
resources/application.properties
upload.path=/var/www/upload/
在application.properties
中,我们有一个upload.path
属性,用于指定上传目录。
resources/static/index.html
<!DOCTYPE html>
<html>
<head>
<title>Uploading file</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h1>Uploading file</h1>
<form action="/doUpload" method="post" enctype="multipart/form-data">
<label>Enter file</label>
<input type="file" name="file">
<button type="submit">Upload</button>
</form>
</body>
</html>
这是主页。 它是src/main/resources/static
目录中的静态文件。 它包含一个用于选择文件并将其发送到 Spring 应用的表单。
<form action="/doUpload" method="post" enctype="multipart/form-data">
我们选择了doUpload
URL 模式。 此表单创建的请求将由 Spring 控制器处理。 enctype
属性指定multipart/form-data
编码类型,这是使用 HTML 格式上传文件所必需的。
<input type="file" name="file">
input
标签的type
属性使用户可以选择文件。
<button type="submit">Upload</button>
最后,这是一个提交按钮。
resources/static/success.html
<!DOCTYPE html>
<html>
<head>
<title>Success</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<p>File successfully uploaded</p>
</body>
</html>
文件成功上传到服务器后,将显示success.html
。
resources/static/failure.html
<!DOCTYPE html>
<html>
<head>
<title>Failure</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<p>Failed to upload file</p>
</body>
</html>
文件上传失败时,将显示failure.html
。
Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
这段代码设置了 Spring Boot 应用。
在本教程中,我们学习了如何在 Spring 应用中上传文件。 您可能也对相关教程感兴趣: Spring Boot @ExceptionHandler
教程, Spring Boot @PathVariable
教程, Spring Boot @RequestParam
教程, Spring Boot REST H2 教程,独立的 Spring 应用, Java 教程。
Spring Boot @ExceptionHandler
Spring Boot @ExceptionHandler
教程展示了如何使用 Spring @ExceptionHandler
处理异常。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。
@ExceptionHandler
是用于在特定处理器类或处理器方法中处理异常的注解。 在 Servlet 环境中,我们可以将@ExceptionHandler
注解与@ResponseStatus
结合起来以定义 HTTP 响应的响应状态。
Spring Boot @ExceptionHandler
示例
在以下应用中,我们演示@ExceptionHandler
的用法。 主页中的 HTML 链接调用控制器的方法,该方法将返回数据或引发异常。
pom.xml
src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ ├── controller
│ │ │ └── MyController.java
│ │ ├── exception
│ │ │ └── MyDataException.java
│ │ └── service
│ │ ├── IDataService.java
│ │ └── MyDataService.java
│ └── resources
│ ├── static
│ │ ├── index.html
│ │ └── showError.html
│ └── templates
│ └── showData.ftl
└── test
└── java
这是 Spring 应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootexceptionhandlerex</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven pom.xml
文件。 spring-boot-starter-freemarker
是 Freemarker 模板引擎的依赖项; spring-boot-maven-plugin
将 Spring 应用打包到可执行的 JAR 或 WAR 归档文件中。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import com.zetcode.exception.MyDataException;
import com.zetcode.service.IDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
import java.util.Map;
@Controller
public class MyController {
@Autowired
private IDataService dataService;
@RequestMapping(value = "/getData")
public ModelAndView getData() {
var data = dataService.findAll();
Map<String, Object> params = new HashMap<>();
params.put("values", data);
return new ModelAndView("showData", params);
}
@ExceptionHandler(MyDataException.class)
public String handleError(MyDataException e) {
return "redirect:/showError.html";
}
}
MyController
的getData()
方法调用服务方法,并将检索到的数据存储到列表中。 数据被发送到showData
视图。 如果是MyDataException
,则控制器将重定向到错误页面。
@ExceptionHandler(MyDataException.class)
public String handleError(MyDataException e) {
return "redirect:/showError.html";
}
handleError()
用@ExceptionHandler
装饰。 MyDataException
的处理器已激活。 在方法的主体中,我们重定向到showError.html
页面。
com/zetcode/exception/MyDataException.java
package com.zetcode.exception;
public class MyDataException extends RuntimeException {
public MyDataException(String message) {
super(message);
}
}
我们定义一个自定义MyDataException
。
com/zetcode/service/IDataService.java
package com.zetcode.service;
import java.util.List;
public interface IDataService {
List<String> findAll();
}
IDataService
包含契约方法。
com/zetcode/service/MyDataService.java
package com.zetcode.service;
import com.zetcode.exception.MyDataException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.springframework.stereotype.Service;
@Service
public class MyDataService implements IDataService {
@Override
public List<String> findAll() {
var r = new Random();
if (r.nextBoolean()) {
throw new MyDataException("Failed to retrieve data");
}
var data = new ArrayList<String>();
data.add("yellow moon");
data.add("brisk pace");
data.add("empty bottle");
data.add("beautiful weather");
return data;
}
}
MyDataService
实现IDataService
的findAll()
方法。 该方法返回数据或抛出MyDataException
。
var r = new Random();
if (r.nextBoolean()) {
throw new MyDataException("Failed to retrieve data");
}
findAll()
方法随机抛出MyDataException
。 然后在控制器中处理异常。
var data = new ArrayList<>();
data.add("yellow moon");
data.add("brisk pace");
data.add("empty bottle");
data.add("beautiful weather");
return data;
如果没有例外,我们将返回一个字符串列表。
resources/static/index.html
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<a href="/getData">Get data</a>
</body>
</html>
这是主页。 它包含一个链接,该链接调用我们的控制器方法以获取一些数据。
resources/static/showError.html
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<p>Failed to retrieve data</p>
</body>
</html>
这是一个错误页面。 抛出MyDataException
时显示。
resources/templates/showData.ftl
<!DOCTYPE html>
<html>
<head>
<title>Data</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h2>Data</h2>
<ul>
<#list values as val>
<li>${val}</td>
</#list>
</ul>
</body>
</html>
showData.ftl
是一个 Freemarker 模板文件,它在 HTML 列表中显示所有检索到的数据。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
是引导 Spring Boot 应用的入口点。
在本教程中,我们展示了如何使用@ExceptionHandler
在 Spring 应用中处理异常。 您可能也对相关教程感兴趣: Spring Boot Flash 属性教程, Spring Boot @ResponseStatus
教程, Spring Boot @PathVariable
教程, Spring Boot @RequestParam
教程, Java 教程,或显示所有 Spring Boot 教程。
Spring Boot @ResponseStatus
Spring Boot @ResponseStatus
教程展示了如何在 Spring 应用中使用@ResponseStatus
注解。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。
@ResponseStatus
@ResponseStatus
用应返回的状态代码和原因消息标记方法或异常类。 调用处理器方法时或抛出指定的异常时,状态代码将应用于 HTTP 响应。 它会覆盖通过其他方式设置的状态信息,例如ResponseEntity
或redirect:
。
Spring Boot @ResponseStatus
示例
在以下应用中,我们演示@ResponseStatus
注解的用法。 该应用模拟用于通过其 ID 检索订单的表单。 尝试查找 ID 大于 500 的订单将引发异常。 由于此异常,将显示一个自定义错误页面。
pom.xml
src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── Application.java
│ │ ├── controller
│ │ │ └── MyController.java
│ │ └── exception
│ │ └── OrderNotFoundException.java
│ └── resources
│ ├── static
│ │ └── index.html
│ └── templates
│ └── error.ftl
└── test
└── java
这是 Spring 应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>springbootresponsestatusex</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven pom.xml
文件。 spring-boot-starter-freemarker
是 Freemarker 模板引擎的依赖项; spring-boot-maven-plugin
将 Spring 应用打包到可执行的 JAR 或 WAR 归档文件中。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import com.zetcode.exception.OrderNotFoundException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MyController {
@RequestMapping(value = "/orders/{id}")
@ResponseBody
public String getOrder(@PathVariable("id") long id) {
if (id < 0 || id > 500) {
var message = String.format("Order %d not found", id);
throw new OrderNotFoundException(message);
}
var message = String.format("Returning order %d", id);
return message;
}
}
MyController
的getOrder()
方法响应客户端请求。 它使用@PathVariable
从路径读取顺序 ID。
if (id < 0 || id > 500) {
var message = String.format("Order %d not found", id);
throw new OrderNotFoundException(message);
}
对于无效订单(id < 0
)和大于 500 的订单,我们抛出OrderNotFoundException
异常。 这是订单系统的简单模拟。
var message = String.format("Returning order %d", id);
对于其他订单 ID,我们返回一条消息,指示已找到并返回了该订单。
com/zetcode/exception/OrderNotFoundException.java
package com.zetcode.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such order")
public class OrderNotFoundException extends RuntimeException {
public OrderNotFoundException(String message) {
super(message);
}
}
我们有一个自定义OrderNotFoundException
。 它以@ResponseStatus
注解装饰。 该值设置为HttpStatus.NOT_FOUND
,并且原因消息显示"No such order"
。 此信息将在错误页面中使用。
resources/static/index.html
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<a href="/orders/505/">Get order with Id 505</a>
</body>
</html>
这是主页。 它包含一个链接,用于查找带有 ID 505 的订单。该文件位于src/main/resources/static
目录中。
resources/templates/error.ftl
<html>
<head>
<title>Error page</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div>
<h1>Error occurred</h1>
<p>${status}: ${error} - ${message}</p>
</div>
</body>
</html>
error.ftl
是通用错误页面。 这是 Freemarker 模板文件,显示状态,错误和原因消息。 这些值是通过@ResponseStatus
设置的。 它位于src/main/resources/templates
目录中。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
是设置 Spring Boot 应用的入口。
图:错误页面
在本教程中,我们展示了如何在 Spring 应用中使用@ResponseStatus
注解。 您可能也对相关教程感兴趣: Spring Boot ResponseEntity
教程, Spring Boot @ExceptionHandler
教程, Java 教程或列出所有 Spring Boot 教程。
Spring Boot ResponseEntity
Spring Boot ResponseEntity
教程展示了如何在 Spring 应用中使用ResponseEntity
。
Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。
ResponseEntity
ResponseEntity
代表 HTTP 响应,包括标头,正文和状态。 尽管@ResponseBody
将返回值放入响应的正文中,但是ResponseEntity
也允许我们添加标头和状态代码。
Spring Boot ResponseEntity
示例
在以下应用中,我们演示ResponseEntity
的用法。 该应用有两种方法:一种方法使用ResponseEntity
创建 HTTP 响应,另一种方法@ResponseBody
。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ ├───controller
│ │ │ MyController.java
│ │ └───model
│ │ Country.java
│ └───resources
│ └───static
│ index.html
└───test
└───java
这是 Spring 应用的项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>responseentityex</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven pom.xml
文件。 spring-boot-starter-parent
是父 POM,它为使用 Maven 构建的应用提供依赖关系和插件管理。 spring-boot-starter-web
是使用 Spring MVC 创建 Spring Boot Web 应用的依赖项。 spring-boot-maven-plugin
将 Spring 应用打包到可执行的 JAR 或 WAR 归档文件中。
com/zetcode/model/Country.java
package com.zetcode.model;
public class Country {
private String name;
private int population;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
}
这是Country
bean。 它具有两个属性:name
和population
。
com/zetcode/controller/MyController.java
package com.zetcode.controller;
import com.zetcode.bean.Country;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MyController {
@RequestMapping(value = "/getCountry")
public ResponseEntity<Country> getCountry() {
var c = new Country();
c.setName("France");
c.setPopulation(66984000);
var headers = new HttpHeaders();
headers.add("Responded", "MyController");
return ResponseEntity.accepted().headers(headers).body(c);
}
@RequestMapping(value = "/getCountry2")
@ResponseBody
public Country getCountry2() {
var c = new Country();
c.setName("France");
c.setPopulation(66984000);
return c;
}
}
控制器包含两种方法。 第一个使用ResponseEntity
,第二个使用@ResponseBody
。
@RequestMapping(value = "/getCountry")
public ResponseEntity<Country> getCountry() {
getCountry()
方法映射到getCountry
URL 模式; 它返回类型为Country
的ResponseEntity
。
var c = new Country();
c.setName("France");
c.setPopulation(66984000);
我们创建一个Country
bean; 此 bean 在响应中返回。
var headers = new HttpHeaders();
headers.add("Responded", "MyController");
我们创建HttpHeaders
的实例并添加新的标头值。
return ResponseEntity.accepted().headers(headers).body(c);
返回ResponseEntity
。 我们给ResponseEntity
一个自定义状态代码,标头和正文。
@RequestMapping(value = "/getCountry2")
@ResponseBody
public Country getCountry2() {
var c = new Country();
c.setName("France");
c.setPopulation(66984000);
return c;
}
使用@ResponseBody
,仅返回正文。 标题和状态代码由 Spring 提供。
resources/static/index.html
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<p>
<a href="getCountry">Get country 1</a>
</p>
<p>
<a href="getCountry2">Get country 2</a>
</p>
</body>
</html>
这是主页。 它包含两个链接。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
是设置 Spring Boot 应用的入口。
$ curl localhost:8080/getCountry -I
HTTP/1.1 202
Responded: MyController
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 17 Jan 2019 21:40:49 GMT
调用第一个方法时,我们可以看到选择的 202 状态代码和自定义标头值。
在本教程中,我们展示了如何在 Spring 应用中使用ResponseEntity
。 您可能也对相关教程感兴趣: Spring Boot @ResponseStatus
教程, Spring Boot @ExceptionHandler
教程, Spring Boot 上传文件, Spring Boot @PathVariable
教程, Spring Boot @RequestParam
教程, Spring Boot REST H2 教程,独立的 Spring 应用, Java 教程。
Spring Boot ApplicationReadyEvent
教程
Spring Boot ApplicationReadyEvent
教程展示了在应用就绪后如何执行任务。
Spring Boot 应用发出各种事件。 我们可以使用监听器对此类事件做出反应。
例如,ApplicationStartedEvent
是在刷新上下文之后但在调用任何应用和命令行运行程序之前发送的。 在调用任何应用和命令行运行程序之后,将发送ApplicationReadyEvent
。 它指示该应用已准备就绪,可以处理请求。
Spring Boot ApplicationReadyEvent
示例
以下 Spring Boot 应用是一个简单的 Web 应用,可响应ApplicationStartedEvent
触发 Web 请求。 该请求是通过WebClient
发出的。
pom.xml
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ ├───bean
│ │ │ TimeResponse.java
│ │ ├───event
│ │ │ AppEvents.java
│ │ └───routes
│ │ AppRoutes.java
│ └───resources
└───test
└───java
└───com
└───zetcode
└───routes
AppRoutesTest.java
这是项目结构。
pom.xml
<?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.zetcode</groupId>
<artifactId>applicationreadyevent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这是 Maven 构建文件。 spring-boot-starter-webflux
是使用 Spring Framework 的反应式 Web 支持构建 WebFlux 应用的入门。
com/zetcode/bean/TimeResponse.java
package com.zetcode.bean;
public class TimeResponse {
private String date;
private Long unixtime;
private String time;
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public Long getUnixtime() {
return unixtime;
}
public void setUnixtime(Long unixtime) {
this.unixtime = unixtime;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("TimeResponse{");
sb.append("date='").append(date).append('\'');
sb.append(", unixtime=").append(unixtime);
sb.append(", time='").append(time).append('\'');
sb.append('}');
return sb.toString();
}
}
这是TimeResponse
bean。 它用于存储来自 Web 请求的数据。
com/zetcode/event/AppEvents.java
package com.zetcode.event;
import com.zetcode.bean.TimeResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@Component
public class AppEvents {
private static final Logger logger = LoggerFactory.getLogger(AppEvents.class);
@EventListener(ApplicationReadyEvent.class)
public void startApp() {
var webClient = WebClient.create("http://time.jsontest.com/");
Mono<TimeResponse> result = webClient.get()
.retrieve()
.bodyToMono(TimeResponse.class);
result.subscribe(res -> logger.info("{}", res));
}
}
在AppEvents
中,我们创建了一个简单的 GET 请求。
@EventListener(ApplicationReadyEvent.class)
public void startApp() {
使用@EventListener
注解,我们注册ApplicationReadyEvent
。
var webClient = WebClient.create("http://time.jsontest.com/");
从http://time.jsontest.com/
中,我们可以获得当前时间。
Mono<TimeResponse> result = webClient.get()
.retrieve()
.bodyToMono(TimeResponse.class);
result.subscribe(res -> logger.info("{}", res));
使用WebClient
,我们向站点创建一个 GET 请求,并将结果输出到终端。
com/zetcode/routes/AppRoutes.java
package com.zetcode.routes;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
@Configuration
public class AppRoutes {
@Bean
RouterFunction<ServerResponse> home() {
return route(GET("/"), request -> ok().syncBody("Home page"));
}
}
我们的应用使用函数式的 Web 框架来返回首页的简单消息。
com/zetcode/Application.java
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application
设置 Spring Boot 应用
com/zetcode/routes/AppRoutesTest.java
package com.zetcode.routes;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class AppRoutesTest {
@Autowired
private WebTestClient client;
@Test
public void test_home_page() {
client.get().uri("/").exchange().expectStatus().isOk()
.expectBody(String.class).isEqualTo("Home page");
}
}
使用WebTestClient
,我们为主页创建一种测试方法。
...
2019-06-21 16:40:45.214 INFO 9356 --- [ctor-http-nio-5] com.zetcode.event.AppEvents : TimeResponse{date='06-21-2019', unixtime=null, time='02:40:47 PM'}
...
当应用启动时,我们将此消息发送到终端。