ZetCode-Spring-Boot-教程-一-

ZetCode Spring Boot 教程(一)

原文:ZetCode

协议:CC BY-NC-SA 4.0

Spring Boot Flash 属性

原文: http://zetcode.com/springboot/flashattribute/

Spring Boot Flash 属性教程展示了如何在 Spring Boot 应用中创建 Flash 消息。

Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。

Flash 消息是用于用户通知或存储表单输入的临时数据。 它们存储在一个会话中,并且一旦检索就消失。

使用RedirectAttributesaddFlashAttribute()在 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 教程

原文: http://zetcode.com/springboot/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是标记注解,指示基础接口是存储库。 通过扩展特定的存储库接口(例如CrudRepositoryPagingAndSortingRepositoryJpaRepository)来创建存储库。

Spring Data 已与 Spring MVC 控制器进行了高级集成,并提供了从存储库方法名称派生的动态查询。

CrudRepository

CrudRepository实现基本的 CRUD 操作,包括countdeltedeleteByIdsavesaveAllfindByIdfindAll

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()方法调用userRepositoryfindAll()方法并检索所有用户。

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> {
}

UserRepositoryCrudRepository延伸。 它提供了实体的类型及其主键。

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 教程

Spring Boot JpaRepository 教程

原文: http://zetcode.com/springboot/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是标记注解,指示基础接口是存储库。 通过扩展特定的存储库接口(例如CrudRepositoryPagingAndSortingRepositoryJpaRepository)来创建存储库。

Spring Data 已与 Spring MVC 控制器进行了高级集成,并提供了从存储库方法名称派生的动态查询。

JpaRepository

JpaRepositoryRepository的 JPA 特定扩展。 它包含CrudRepositoryPagingAndSortingRepository的完整 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实体。 它包含以下属性:idnamepopulation

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> {

}

CityRepositoryJpaRepository延伸。 它提供了实体的类型及其主键。

注意:在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 教程

Spring Boot findById 教程

原文: http://zetcode.com/springboot/findbyid/

Spring Boot findById教程展示了如何使用CrudRepositoryfindById方法通过其 ID 检索实体。

Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架的演进,可帮助您轻松创建独立的,生产级的基于 Spring 的应用。

CrudRepository

CrudRepository接口在存储库中为特定类型提供通用 CRUD 操作。 其findById()方法通过其 ID 检索实体。 返回值为Optional<T>

Optional<T>是一个容器对象,可能包含也可能不包含非空值。 如果存在值,则isPresent()返回trueget()返回该值。 如果存在该值,则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
...

我们运行该应用。

在本教程中,我们展示了如何使用CrudRepositoryfindById()方法查找特定实体。 您可能也对相关教程感兴趣:

列出所有 Spring Boot 教程

Spring Boot Data JPA @NamedQuery教程

原文: http://zetcode.com/springboot/datajpanamedquery/

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教程

原文: http://zetcode.com/springboot/datajpaquery/

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 教程

Spring Boot Querydsl 教程

原文: http://zetcode.com/springboot/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);
    }
}

我们使用EntityManagerCityRepository创建 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 教程

Spring Boot Data JPA 排序教程

原文: http://zetcode.com/springboot/datajpasort/

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教程

原文: http://zetcode.com/springboot/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 教程

Spring Boot TestEntityManager 教程

原文: http://zetcode.com/springboot/testentitymanager/

Spring Boot TestEntityManager教程展示了如何在 JPA 测试中使用TestEntityManagerTestEntityManager提供了EntityManager方法的子集,可用于测试以及用于常见测试任务(例如persistfind)的辅助方法。

Spring 是用于创建企业应用的流行 Java 应用框架。 Spring Boot 是 Spring 框架的演进,可帮助您轻松创建独立的,生产级的基于 Spring 的应用。

TestEntityManager

TestEntityManager允许在测试中使用EntityManager。 Spring RepositoryEntityManager的抽象; 它使开发者免受 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));

我们用EntityManagerpersist()方法保存了四个城市。

var cities = repository.findByName("Bratislava");
assertEquals(1, cities.size());

我们测试findByName()方法返回一个城市。

assertThat(cities).extracting(City::getName).containsOnly("Bratislava");

在这里,我们测试城市的名称。

$ mvn spring-boot:test

我们运行测试。

在本教程中,我们在测试中使用了TestEntityManager

列出所有 Spring Boot 教程

Spring Boot 发送电子邮件教程

原文: http://zetcode.com/springboot/email/

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);
    }
}

电子邮件服务使用JavaMailSenderSimpleMailMessage发送简单的电子邮件。

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 派生的查询

原文: http://zetcode.com/springboot/datajpaderivedqueries/

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 从具有属性组合的特定关键字创建查询; 例如:findByAgeLessThanfindByFirstnameEndingWithfindByFirstnameEquals。 关键字列表在 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 教程

Spring Boot Data JPA 示例查询

原文: http://zetcode.com/springboot/datajpaquerybyexample/

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 教程

Spring Boot Jersey 教程

原文: http://zetcode.com/springboot/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 CSV 教程

原文: http://zetcode.com/articles/springbootcsv/

在本教程中,我们将在 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.H2Dialectddl-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);

我们称cityServicefindAll()来获取所有城市。 我们将 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 库。 您可能也对相关教程感兴趣:

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 中加载资源

原文: http://zetcode.com/springboot/loadresource/

在本教程中,我们将展示如何在 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 应用中的资源。 我们使用@ValueResourceLoader加载资源文件。

列出所有 Spring Boot 教程

Spring Boot H2 REST 教程

原文: http://zetcode.com/articles/springbootresth2/

在本教程中,我们将使用 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}.sqldata-${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 会自动选择MappingJackson2HttpMessageConverterCity实例转换为 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

原文: http://zetcode.com/springboot/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-serverfakerfs模块。 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-starterspring-webjackson-databindspring-boot-starter-testspring-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。 设置RestTemplateSimpleClientHttpRequestFactory用于设置连接和读取超时。

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 教程

原文: http://zetcode.com/springboot/restxml/

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.H2Dialectddl-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,我们将namepopulation属性设置为 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 Moustache 教程

http://zetcode.com/articles/springbootmustache/

在 Spring Boot Mustache 教程中,我们将使用 Mustache 模板引擎和 HSQLDB 数据库创建一个简单的 Spring Boot Web 应用。

Spring 是流行的 Java 应用框架。 Spring Boot 致力于创建独立的,基于生产级别的基于 Spring 的应用,而无任何麻烦。

HSQLDB 是完全用 Java 创建的开源关系数据库管理系统。 它提供了一个小型,快速的多线程事务型数据库引擎,具有基于内存和基于磁盘的表,并支持嵌入式和服务器模式。 它包括一个功能强大的命令行 SQL 工具和简单的 GUI 查询工具。

Moustache

Moustache 是一个简单的 Web 模板系统。 它可用于许多编程语言,包括 Java。 由于没有任何显式的控制流语句(例如ifelse条件语句或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}.sqldata-${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()方法的实现。 我们借助JdbcTemplateCARS表中检索所有汽车。

@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注解启用自动配置和组件扫描。

Listing cars

图:列出汽车

该应用部署在内置的 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 教程

原文: http://zetcode.com/springboot/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 项目提供两种类型的发布者:MonoFluxFlux是产生 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()方法的返回类型是PublisherMono.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 配置

原文: http://zetcode.com/articles/springbootthymeleafconf/

在 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 自动控制器

http://zetcode.com/articles/springbootautocontroller/

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 教程

原文: http://zetcode.com/springboot/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}.sqldata-${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()方法的实现。 我们借助JdbcTemplatecities表中检索所有城市。

@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注解启用自动配置和组件扫描。

Listing cities

图:列出城市

该应用部署在内置的 Tomcat 服务器上,该服务器监听端口 8080。

在本教程中,我们使用 FreeMarker 和 H2 创建了一个 Spring Boot Web 应用。 您可能也对相关教程感兴趣: FreeMarker 教程Java 教程或列出所有 Spring Boot 教程

Spring Boot Environment

原文: http://zetcode.com/articles/springbootenvironment/

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"));

也可以使用Environmentapplication.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 集成教程

原文: http://zetcode.com/articles/springbootswing/

在 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 中提供图像文件

原文: http://zetcode.com/articles/springbootserveimage/

在本教程中,我们展示了如何在 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的主体。

使用ResponseEntityInputStreamResource提供图像

在第三种情况下,我们使用ResponseEntityInputStreamResourceInputStreamResource是 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 报告

原文: http://zetcode.com/springboot/servepdf/

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 报告。 它使用ResponseEntityInputStreamResource将 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@Idspring.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 基本注解

原文: http://zetcode.com/springboot/annotations/

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注解标记要插入CityRepositorycityRepository字段。

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 教程

Spring Boot @ResponseBody教程

原文: http://zetcode.com/springboot/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。 它具有idnamepopulation属性。

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教程

原文: http://zetcode.com/springboot/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中,我们使用ViewControllerRegistryaddViewController()方法注册了一条新路由。

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教程

原文: http://zetcode.com/springboot/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 教程

原文: http://zetcode.com/articles/springbootrestdatajpa/

在本教程中,我们将使用 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.H2Dialectddl-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 自动选择MappingJackson2HttpMessageConverterCity实例转换为 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 @RequestParam教程

原文: http://zetcode.com/springboot/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

原文: http://zetcode.com/articles/springbootlistbeans/

在本教程中,我们列出了存储在 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,并同时拾取MyBeanMyRunner

$ 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 BeanSpring Boot @Qualifier注解在 Spring Boot 中提供静态内容Spring Boot DataSourceBuilder教程Spring Boot iText 教程Java 教程

Spring Boot @Bean

原文: http://zetcode.com/articles/springbootbean/

在 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教程

原文: http://zetcode.com/springboot/qualifier/

Spring Boot @Qualifier教程展示了如何使用@Qualifier来区分相同类型的 bean。 它也可以用于注解其他自定义注解,这些注解随后可以用作限定符。

Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可帮助您以最少的精力创建独立的,生产级的基于 Spring 的应用。

以下三个应用是命令行 Spring Boot 应用。

@Qualifier Person bean

在我们的应用中,我们有两个Person类型的 bean:StudentManager。 我们使用@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.xmlPerson.javaApplication.javaMyRunner.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 中提供静态内容

原文: http://zetcode.com/springboot/static/

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 错误

原文: http://zetcode.com/springboot/whitelabelerror/

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-webspring-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 教程

Spring Boot DataSourceBuilder 教程

原文: http://zetcode.com/springboot/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}.sqldata-${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 教程

原文: http://zetcode.com/articles/springbooth2/

在 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}.sqldata-${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()方法的实现。 我们借助JdbcTemplateCars表中检索所有汽车。

@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教程

原文: http://zetcode.com/springboot/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模型,具有以下属性:idnamepopulation

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 集成

原文: http://zetcode.com/articles/jasperspringbootweb/

在本教程中,我们展示如何在 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()方法的实现。 我们借助JdbcTemplateCARS表中检索所有汽车。

@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 教程

原文: http://zetcode.com/articles/springbootitext/

在本教程中,我们将展示如何使用 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}.sqldata-${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()方法的实现。 我们借助JdbcTemplateCars表中检索所有汽车。

@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 集成

原文: http://zetcode.com/articles/jasperspringbootcmd/

在本教程中,我们展示如何在 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文件包含以下依赖项:derbyclientjasperreportsspring-boot-starter-jdbcjasperreports依赖项是 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()方法的实现。 我们借助JdbcTemplateCARS表中检索所有汽车。

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 应用

原文: http://zetcode.com/articles/springbootrestsimple/

在本教程中,我们将创建一个简单的 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 自动选择MappingJackson2HttpMessageConverterCountry实例转换为 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 应用

原文: http://zetcode.com/articles/springbootwebfirst/

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

原文: http://zetcode.com/springboot/groovycli/

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 上传文件

原文: http://zetcode.com/springboot/uploadfile/

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

原文: http://zetcode.com/springboot/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";
    }
}

MyControllergetData()方法调用服务方法,并将检索到的数据存储到列表中。 数据被发送到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实现IDataServicefindAll()方法。 该方法返回数据或抛出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

原文: http://zetcode.com/springboot/responsestatus/

Spring Boot @ResponseStatus教程展示了如何在 Spring 应用中使用@ResponseStatus注解。

Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。

@ResponseStatus

@ResponseStatus用应返回的状态代码和原因消息标记方法或异常类。 调用处理器方法时或抛出指定的异常时,状态代码将应用于 HTTP 响应。 它会覆盖通过其他方式设置的状态信息,例如ResponseEntityredirect:

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;
    }
}

MyControllergetOrder()方法响应客户端请求。 它使用@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 应用的入口。

Error page

图:错误页面

在本教程中,我们展示了如何在 Spring 应用中使用@ResponseStatus注解。 您可能也对相关教程感兴趣: Spring Boot ResponseEntity教程Spring Boot @ExceptionHandler教程Java 教程或列出所有 Spring Boot 教程

Spring Boot ResponseEntity

原文: http://zetcode.com/springboot/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。 它具有两个属性:namepopulation

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 模式; 它返回类型为CountryResponseEntity

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 教程

原文: http://zetcode.com/springboot/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'}
...

当应用启动时,我们将此消息发送到终端。

列出所有 Spring Boot 教程

posted @ 2024-10-24 18:18  绝不原创的飞龙  阅读(3)  评论(0编辑  收藏  举报