ZetCode-Spring-Boot-教程-二-

ZetCode Spring Boot 教程(二)

原文:ZetCode

协议:CC BY-NC-SA 4.0

Spring Boot @Controller

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

Spring Boot @Controller教程显示了如何在 Spring 应用中使用@Controller注解来构建 Web 控制器。

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

Spring MVC

Spring MVC 是基于 Servlet API 构建的原始 Web 框架。 它基于流行的 MVC 设计模式。 MVC(模型-视图-控制器)是一种软件架构模式,它将应用分为三个区域:模型,视图和控制器。 该模型表示一个携带数据的 Java 对象。 该视图表示模型包含的数据的可视化。 控制器控制数据流入模型对象并在数据更改时更新视图。 它使视图和模型分离。

Spring Framework 5.0 引入了一个名为 Spring WebFlux 的并行反应式栈 Web 框架。

@Controller

@Controller注解指示已注解的类是控制器。 它是@Component的特化,可通过类路径扫描自动检测。 它通常与基于@RequestMapping注解的注解处理器方法结合使用。 @RestController是用于创建 Restful 控制器的同级便利注解。

Spring Boot @Controller示例

在以下应用中,我们演示@Controller的用法。 该应用将当前数据和时间返回给客户端。

$ tree
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           ├── Application.java
    │   │           └── controller
    │   │               └── MyController.java
    │   └── resources
    │       ├── static
    │       │   └── index.html
    │       └── templates
    │           └── showMessage.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>controllerex</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-parent是父 POM,它为使用 Maven 构建的应用提供依赖关系和插件管理。 spring-boot-starter-freemarker是 Freemarker 模板引擎的依赖项。 此依赖项还将在项目中包含 Spring MVC。 spring-boot-maven-plugin将 Spring 应用打包到可执行的 JAR 或 WAR 归档文件中。

com/zetcode/MyController.java

package com.zetcode.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;

@Controller
public class MyController {

    @RequestMapping(value = "/getDateAndTime")
    public ModelAndView getDateAndTime() {

        var now = LocalDateTime.now();
        var dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
        var date_time = dtf.format(now);

        var params = new HashMap<String, Object>();
        params.put("date_time", date_time);

        return new ModelAndView("showMessage", params);
    }
}

这是MyController。 它响应来自客户端的请求。 它找出当前日期和时间,并将处理过程解析为showMessage.ftl模板,并将其传递给数据。

@Controller
public class MyController {

MyController带有@Controller注解。

@RequestMapping(value = "/getDateAndTime")
public ModelAndView getDateAndTime() {

getDateAndTime()方法映射到getDateAndTime URL 模式; 它返回ModelAndView,它是 Web MVC 框架中 Model 和 View 的持有者。

var now = LocalDateTime.now();
var dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
var date_time = dtf.format(now);

我们获取并格式化本地日期和时间。

var params = new HashMap<String, Object>();
params.put("date_time", date_time);

日期和时间字符串将添加到参数映射中。

return new ModelAndView("showMessage", params);

我们返回ModelAndView。 由于 POM 文件中存在 Freemarker 依赖项,因此 Spring 将处理解析为showMessage.ftl模板文件,并将params对象传递给它。

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="getDateAndTime">Get date and time</a>
        </p>
    </body>
</html>

这是主页。 它包含一个调用 Spring 控制器的链接。 它是静态资源,位于预定义的src/main/resources/static目录中。

resources/templates/showMessage.ftl

<!DOCTYPE html>
<html>
<head>
    <title>Show message</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>

Date and time: ${date_time}    

</body>
</html>

showMessage.ftl是一个 Freemarker 模板文件。 它位于预定义的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 应用的入口。 @SpringBootApplication注解启用自动配置和组件扫描。 在扫描过程中,将查找@Controller注解,并从MyController类创建一个 Spring bean。

$ mvn spring-boot:run 

应用运行后,我们可以导航到localhost:8080

在本教程中,我们展示了如何在 Spring 应用中使用@Controller注解。 您可能也对相关教程感兴趣: Spring Boot @RestController教程Java Servlets 教程Spring Boot @ResponseStatus教程Spring Boot @ExceptionHandler教程Spring Boot 上传文件Spring Boot @Component教程Spring Boot @RequestParam教程Java 教程

Spring Boot @RestController

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

Spring Boot @RestController教程展示了如何在 Spring 应用中使用@RestController注解来构建 Restful 控制器。

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

Spring MVC

Spring MVC 是基于 Servlet API 构建的主要 Web 框架。 它基于流行的 MVC 设计模式。 MVC(模型-视图-控制器)是一种软件架构模式,它将应用分为三个区域:模型,视图和控制器。 该模型表示一个携带数据的 Java 对象。 该视图表示模型包含的数据的可视化。 控制器控制数据流到模型对象中,并在数据更改时更新视图。 它将视图和模型分开。

Spring Framework 5.0 引入了一个名为 Spring WebFlux 的并行反应式栈 Web 框架。

@RestController

@RestController是用于创建 Restful 控制器的便捷注解。 它是@Component的特化,可通过类路径扫描自动检测。 它添加了@Controller@ResponseBody注解。 它将响应转换为 JSON 或 XML。 它不适用于视图技术,因此方法无法返回ModelAndView。 它通常与基于@RequestMapping注解的注解处理器方法结合使用。

@Controller注解与视图技术一起使用。

RESTFul 应用

RESTFul 应用遵循 REST 架构样式,该样式用于设计网络应用。 RESTful 应用生成对资源执行 CRUD(创建/读取/更新/删除)操作的 HTTP 请求。 RESTFul 应用通常以 JSON 或 XML 格式返回数据。

Spring Boot @RestController示例

在以下应用中,我们演示@RestController的用法。 该应用返回城市列表作为 JSON 数据。

pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           ├───controller
│   │           │       MyController.java
│   │           ├───model
│   │           │       City.java
│   │           └───service
│   │                   CityService.java
│   │                   ICityService.java
│   └───resources
│       └───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>springbootrestcontrollerex</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 pom.xml文件。 spring-boot-starter-parent是父 POM,它为使用 Maven 构建的应用提供依赖关系和插件管理。 spring-boot-starter-web是使用 Spring MVC 构建 Web(包括 RESTful)应用的入门工具。 它使用 Tomcat 作为默认的嵌入式容器。 spring-boot-maven-plugin将 Spring 应用打包到可执行的 JAR 或 WAR 归档文件中。

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/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> getCities() {

        List<City> cities = cityService.findAll();

        return cities;
    }
}

这是MyController。 它以 JSON 格式返回城市列表。

@RestController
public class MyController {

MyController带有@RestController注解。

@Autowired
private ICityService cityService;

我们用@Autowired注解注入CityService

@GetMapping(value = "/cities")
public List<City> getCities() {

getCities()方法映射到getCities URL 模式; 它返回城市列表,并通过消息转换器将其转换为 JSON。

com/zetcode/service/ICityService.java

package com.zetcode.service;

import com.zetcode.bean.City;
import java.util.List;

public interface ICityService {

    public List<City> findAll();
}

ICityService包含findAll()合约方法。

com/zetcode/service/CityService.java

package com.zetcode.service;

import com.zetcode.model.City;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;

@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包含findAll()方法的实现。

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="cities">Get all cities</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注解启用自动配置和组件扫描。 在扫描过程中,将查找@RestController注解,并从MyController类创建一个 Spring bean。

$ mvn spring-boot:run 

应用运行后,我们可以导航到localhost:8080

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

Spring Boot @PostConstruct

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

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

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

@PostConstruct

@PostConstruct是在依赖注入完成以执行任何初始化之后需要执行的方法上使用的注解。

Spring Boot @PostConstruct示例

以下应用演示了@PostConstruct的用法。 它使用注解创建两个日志方法,在初始化它们的 bean 之后调用它们。 这些消息在应用运行后显示。 应用本身向客户端发送一条消息。 从配置文件中读取文本消息。

$ tree
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           ├── Application.java
    │   │           ├── controller
    │   │           │   └── MyController.java
    │   │           └── service
    │   │               ├── IMessageService.java
    │   │               └── MessageService.java
    │   └── resources
    │       ├── application.properties
    │       └── 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>SpringBootPostConstruct</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-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 构建 Web(包括 RESTful)应用的入门工具。 它使用 Tomcat 作为默认的嵌入式容器。 spring-boot-maven-plugin将 Spring 应用打包到可执行的 JAR 或 WAR 归档文件中。

application.properties

my.msg=Hello there

spring.main.banner-mode=off
logging.level.org.springframework=ERROR

application.properties是 Spring Boot 中的主要配置文件。 我们设置了一个message属性,该属性将由应用返回给客户端。 我们关闭 Spring 横幅并减少 Spring 框架的日志记录量。

MyController.java

package com.zetcode.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.zetcode.service.IMessageService;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@RestController
public class MyController {

    private static final Logger logger = LoggerFactory.getLogger(MyController.class);

    @Autowired
    IMessageService messageService;

    @RequestMapping(value = "/getMessage")
    public String getMessage() {

        String message = messageService.getMessage();

        return message;
    }

    @PostConstruct
    public void doLog() {
        logger.info("Info message in MyController");
    }
}

这是MyController。 它向客户端发送一条消息。

@RequestMapping(value = "/getMessage")
public String getMessage() {

    String message = messageService.getMessage();

    return message;
}

从消息服务生成一条消息,并将其返回给客户端。

@PostConstruct
public void doLog() {
    logger.info("Info message in MyController");
}

doLog()方法用@PostConstruct注解修饰。 在初始化MyController bean 之后调用该方法。 它记录一条简单的参考消息。

IMessageService.java

package com.zetcode.service;

public interface IMessageService {

    public String getMessage();
}

IMessageService包含getMessage()合约方法。

MessageService.java

package com.zetcode.service;

import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class MessageService implements IMessageService {

    private static final Logger logger = LoggerFactory.getLogger(MessageService.class);

    @Value(value = "${my.msg}")
    private String message;

    @Override
    public String getMessage() {

        return message;
    }

    @PostConstruct
    public void doLog() {
        logger.info("Info message in MessageService");
    }
}

MessageService包含getMessage()方法的实现。

@Value(value = "${my.msg}")
private String message;

从带有@Value注解的application.properties文件中读取返回给客户端的消息,并将其设置为message字段。

@Override
public String getMessage() {

    return message;
}

getMessage()返回消息字符串。

@PostConstruct
public void doLog() {
    logger.info("Info message in MessageService");
}

MessageService还包含用@PostConstruct装饰的doLog()方法。 在 bean 初始化之后调用它。

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="getMessage">Get Message</a>
        </p>
    </body>
</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);
    }
}

Application是设置 Spring Boot 应用的入口。 @SpringBootApplication注解启用自动配置和组件扫描。

$ mvn -q spring-boot:run 
...
2017-12-14 21:35:30.788  INFO 10665 --- [main] com.zetcode.service.MessageService  : Info message in MessageService
2017-12-14 21:35:30.791  INFO 10665 --- [main] com.zetcode.controller.MyController : Info message in MyController
...

应用运行后,我们可以在控制台上看到这两个日志消息。

在本教程中,我们展示了如何在 Spring 应用中使用@PostConstruct注解。 您可能也对相关教程感兴趣: Java Servlets 教程Spring Boot @Controller教程Spring Boot @ExceptionHandler教程Spring Boot 上传文件Spring Boot @PathVariable教程Spring Boot @RequestParam教程Spring Boot @ResponseBody教程Java 教程

Spring Boot @Component

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

Spring Boot @Component教程显示了如何在 Spring 应用中使用@Component注解。 在示例中,我们创建一个 Spring Boot 控制台应用。

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

@Component

@Component是最通用的 Spring 注解。 在类路径扫描期间找到装饰有@Component的 Java 类,并在上下文中注册为 Spring Bean。 @Service@Repository@Controller@Component的专业,用于更具体的情况。

@ComponentScan确保找到用@Component装饰的类并将其注册为 Spring Bean。 @ComponentScan自动包含在@SpringBootApplication中。

@Bean具有与@Component类似的目的。 不会自动检测到。 用@Bean装饰的方法会在配置阶段生成一个由 Spring 容器管理的 bean。

Spring Boot @Component示例

以下应用演示了@Component的用法。 它使用注解创建一个随机生成名称的 bean。

pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           │   MyRunner.java
│   │           └───service
│   │                   RandomNameGenerator.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>springbootcomponentex</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 pom.xml文件。 spring-boot-starter-parent是父 POM,它为使用 Maven 构建的应用提供依赖关系和插件管理。 spring-boot-starter是一个核心启动器,包括自动配置支持,日志记录和 YAML。 spring-boot-maven-plugin将 Spring 应用打包到可执行的 JAR 或 WAR 归档文件中。

resources/application.properties

spring.main.banner-mode=off
logging.level.org.springframework=ERROR
logging.pattern.console=%d{dd-MM-yyyy HH:mm:ss} %magenta([%thread]) %highlight(%-5level) %logger.%M - %msg%n

application.properties是 Spring Boot 中的主要配置文件。 我们关闭 Spring 横幅,通过仅选择错误消息来减少 Spring 框架的日志记录数量,并设置控制台日志记录模式

com/zetcode/service/RandomNameGenerator.java

package com.zetcode.service;

import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Random;

@Component
public class RandomNameGenerator {

    public String generate() {

        var names = List.of("Peter", "Roland", "Lucy", "Robert", "Jane");

        var r = new Random();
        int i = r.nextInt(names.size());

        return names.get(i);
    }
}

RandomNameGenerator是装饰有@Component的 Java 类。 在组件扫描过程中将检测到它,并将其注册为 Spring Bean。

com/zetcode/MyRunner.java

package com.zetcode;

import com.zetcode.service.RandomNameGenerator;
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 RandomNameGenerator randGenerator;

    @Override
    public void run(String... args) {

        logger.info("Generating random name: {}", randGenerator.generate());
        logger.info("Generating random name: {}", randGenerator.generate());
        logger.info("Generating random name: {}", randGenerator.generate());
    }
}

通过实现CommandLineRunner,将在应用启动后执行MyRunner类的run()方法。

@Component
public class MyRunner implements CommandLineRunner {

MyRunner也装饰有@Component,因此也会自动检测并注册。

@Autowired
private RandomNameGenerator randGenerator;

使用@Autowired注解,我们将RandomNameGenerator bean 注入到randGenerator字段中。

@Override
public void run(String... args) {

    logger.info("Generating random name: {}", randGenerator.generate());
    logger.info("Generating random name: {}", randGenerator.generate());
    logger.info("Generating random name: {}", randGenerator.generate());
}

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 应用的入口。 @SpringBootApplication注解启用自动配置和组件扫描。 它是@Configuration@EnableAutoConfiguration@ComponentScan注解的便捷注解。

$ mvn -q spring-boot:run 
...
30-04-2019 12:22:44 [main] INFO  com.zetcode.MyRunner.run - Generating random name: Roland
30-04-2019 12:22:44 [main] INFO  com.zetcode.MyRunner.run - Generating random name: Peter
30-04-2019 12:22:44 [main] INFO  com.zetcode.MyRunner.run - Generating random name: Lucy

应用运行后,我们可以在控制台中看到日志消息。

在本教程中,我们展示了如何在 Spring 应用中使用@Component注解。 您可能也对相关教程感兴趣: Spring Boot @Controller教程Spring Boot @Repository教程Java 教程或列出全部 Spring 入门教程。

Spring Boot @ConfigurationProperties教程

http://zetcode.com/springboot/configurationproperties/

Spring Boot @ConfigurationProperties教程展示了如何在 Spring Boot 应用中使用@ConfigurationProperties将属性绑定到对象。

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

@ConfigurationProperties

@ConfigurationProperties允许轻松地将整个属性和 Yaml 文件映射到一个对象。 它还允许使用 JSR-303 bean 验证来验证属性。 默认情况下,注解从application.properties文件中读取。 可以使用@PropertySource注解来更改源文件。

Spring Boot @ConfigurationProperties示例

以下应用从application.properties文件读取配置数据,该文件是默认的 Spring Boot 配置文件。

pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           │   MyRunner.java
│   │           └───conf
│   │                   AppProperties.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>springbootconfigurationproperties</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 pom.xml文件。

resources/application.properties

spring.main.banner-mode=off

app.colour=steelblue
app.lang=en
app.theme=dark

application.properties文件中,我们具有三个自定义属性。 它们具有app前缀。

com/zetcode/conf/AppProperties.java

package com.zetcode.conf;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "app")
public class AppProperties {

    private String colour;
    private String lang;
    private String theme;

    public String getColour() {
        return colour;
    }

    public void setColour(String colour) {
        this.colour = colour;
    }

    public String getLang() {
        return lang;
    }

    public void setLang(String lang) {
        this.lang = lang;
    }

    public String getTheme() {
        return theme;
    }

    public void setTheme(String theme) {
        this.theme = theme;
    }
}

这些属性将绑定到此配置对象。

@Configuration
@ConfigurationProperties(prefix = "app")
public class AppProperties {

@Configuration注解使它成为 Spring 管理的 bean。 在@ConfigurationProperties中,我们为属性设置前缀。

com/zetcode/MyRunner.java

package com.zetcode;

import com.zetcode.conf.AppProperties;
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(Application.class);

    @Autowired
    private AppProperties appProperties;

    @Override
    public void run(String... args) throws Exception {

        logger.info("Colour: {}", appProperties.getColour());
        logger.info("Language: {}", appProperties.getLang());
        logger.info("Theme: {}", appProperties.getTheme());
    }
}

MyRunner中,我们将AppProperties注入字段中并读取其值。

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 Boot @ConfigurationProperties示例 II

在第二个应用中,我们还将验证属性。

pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           │   MyRunner.java
│   │           └───config
│   │                   MailProperties.java
│   └───resources
│           application.properties
│           mail.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>springbootconfigurationpropertiesvalidation</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>

        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

这是pom.xml文件。 我们还有一个hibernate-validator依赖项。

resources/application.properties

spring.main.banner-mode=off

这是application.properties文件。

resources/mail.properties

hostname=info@example.com
port=9000
from=admin@example.com

recipients[0]=user1@example.com
recipients[1]=user2@example.com
recipients[2]=user3@example.com
recipients[3]=user4@example.com

我们有一个自定义的mail.properties文件。

com/zetcode/config/MailProperties.java

package com.zetcode.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.List;

@Configuration
@PropertySource("classpath:mail.properties")
@ConfigurationProperties
@Validated
public class MailProperties {

    @NotNull
    private String hostname;

    @Min(1000)
    @Max(10000)
    private int port;

    @NotNull
    private String from;

    @NotNull
    private List<String> recipients;

    public String getHostname() {
        return hostname;
    }

    public void setHostname(String hostname) {
        this.hostname = hostname;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public List<String> getRecipients() {
        return recipients;
    }

    public void setRecipients(List<String> recipients) {
        this.recipients = recipients;
    }
}

我们使用@PropertySource注解设置自定义属性文件的路径。 @Validated注解验证属性。

com/zetcode/MyRunner.java

package com.zetcode;

import com.zetcode.config.MailProperties;
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(Application.class);

    @Autowired
    private MailProperties mailProperties;

    @Override
    public void run(String... args) throws Exception {

        logger.info("Hostname: {}", mailProperties.getHostname());
        logger.info("Port: {}", mailProperties.getPort());
        logger.info("From: {}", mailProperties.getFrom());
        logger.info("Recipients: {}", mailProperties.getRecipients());
    }
}

我们注入MailProperties并以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类。

我们使用mvn -q spring-boot:run运行该应用。

在本教程中,我们展示了如何使用@ConfigurationProperties从外部文件读取配置属性。 您可能也对相关教程感兴趣: Spring PropertySource教程Spring Boot CommandLineRunner教程Java 教程或列出所有 Spring Boot 教程

Spring Boot @Repository

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

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

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

@Repository

@Repository是 Spring 注解,指示装饰的类是存储库。 存储库是一种用于封装存储,检索和搜索行为的机制,该机制模仿对象的集合。 它是@Component注解的一种特殊功能,允许通过类路径扫描自动检测实现类。

@ComponentScan确保找到用@Component和其派生词包括@Repository装饰的类并将其注册为 Spring Bean。 @ComponentScan自动包含在@SpringBootApplication中。

Spring Boot @Repository示例

以下应用演示了@Repository的用法。 它向用户显示 HTML 表中的国家/地区列表。 在本应用中,我们使用 Spring Boot 版本 2.1.0。

pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           ├───controller
│   │           │       MyController.java
│   │           ├───model
│   │           │       Country.java
│   │           ├───repository
│   │           │       CountryRepository.java
│   │           └───service
│   │                   CountryService.java
│   │                   ICountryService.java
│   └───resources
│       │   application.yml
│       │   import.sql
│       ├───static
│       │       index.html
│       └───templates
│               showCountries.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>repositoryex</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>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-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 构建文件。 h2依赖项包括 H2 数据库驱动程序。

Spring Boot 启动器是一组方便的依赖项描述符,可以极大地简化 Maven 配置。 spring-boot-starter-parent具有 Spring Boot 应用的一些常用配置。 spring-boot-devtools包含一些 Spring Boot 开发者工具。 spring-boot-starter-web支持经典和 RESTFul Web 应用。 spring-boot-starter-web-freemarker是使用 Freemarker 模板引擎构建 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 应用。

resources/application.yml

server:
  port: 8086
  servlet:
    context-path: /SpringBootRepository

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/SpringBootRepository/访问该应用。 使用banner-mode属性,我们可以关闭 Spring 横幅。

JPA database值指定要操作的目标数据库。 在本例中,我们指定了 Hibernate 方言org.hibernate.dialect.H2Dialectddl-auto是数据定义语言模式; create-drop选项将自动创建和删除数据库模式。

H2 数据库在内存中运行。 另外,我们将 spring 框架的日志记录级别设置为ERROR。 在application.yml文件位于中src/main/resources目录。

resources/import.sql

INSERT INTO countries(name, population) VALUES('China', 1382050000);
INSERT INTO countries(name, population) VALUES('India', 1313210000);
INSERT INTO countries(name, population) VALUES('USA', 324666000);
INSERT INTO countries(name, population) VALUES('Indonesia', 260581000);
INSERT INTO countries(name, population) VALUES('Brazil', 207221000);
INSERT INTO countries(name, population) VALUES('Pakistan', 196626000);
INSERT INTO countries(name, population) VALUES('Nigeria', 186988000);
INSERT INTO countries(name, population) VALUES('Bangladesh', 162099000);
INSERT INTO countries(name, population) VALUES('Nigeria', 186988000);
INSERT INTO countries(name, population) VALUES('Russia', 146838000);
INSERT INTO countries(name, population) VALUES('Japan', 126830000);
INSERT INTO countries(name, population) VALUES('Mexico', 122273000);
INSERT INTO countries(name, population) VALUES('Philippines', 103738000);
INSERT INTO countries(name, population) VALUES('Ethiopia', 101853000);
INSERT INTO countries(name, population) VALUES('Vietnam', 92700000);
INSERT INTO countries(name, population) VALUES('Egypt', 92641000);
INSERT INTO countries(name, population) VALUES('Germany', 82800000);
INSERT INTO countries(name, population) VALUES('the Congo', 82243000);
INSERT INTO countries(name, population) VALUES('Iran', 82800000);
INSERT INTO countries(name, population) VALUES('Turkey', 79814000);
INSERT INTO countries(name, population) VALUES('Thailand', 68147000);
INSERT INTO countries(name, population) VALUES('France', 66984000);
INSERT INTO countries(name, population) VALUES('United Kingdom', 60589000);
INSERT INTO countries(name, population) VALUES('South Africa', 55908000);
INSERT INTO countries(name, population) VALUES('Myanmar', 51446000);
INSERT INTO countries(name, population) VALUES('South Korea', 68147000);
INSERT INTO countries(name, population) VALUES('Colombia', 49129000);
INSERT INTO countries(name, population) VALUES('Kenya', 47251000);
INSERT INTO countries(name, population) VALUES('Spain', 46812000);
INSERT INTO countries(name, population) VALUES('Argentina', 43850000);
INSERT INTO countries(name, population) VALUES('Ukraine', 42603000);
INSERT INTO countries(name, population) VALUES('Sudan', 41176000);
INSERT INTO countries(name, population) VALUES('Algeria', 40400000);
INSERT INTO countries(name, population) VALUES('Poland', 38439000);

模式是由 Hibernate 自动创建的。 之后,将执行import.sql文件以将数据填充到表中。

com/zetcode/model/Country.java

package com.zetcode.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Objects;

@Entity
@Table(name = "countries")
public class Country {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private int population;

    public Country() {
    }

    public Country(Long id, String name, int population) {
        this.id = id;
        this.name = name;
        this.population = population;
    }

    public Long getId() {
        return 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;
        Country country = (Country) o;
        return population == country.population &&
                Objects.equals(id, country.id) &&
                Objects.equals(name, country.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, population);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Country{");
        sb.append("id=").append(id);
        sb.append(", name='").append(name).append('\'');
        sb.append(", population=").append(population);
        sb.append('}');
        return sb.toString();
    }
}

这是Country实体。 每个实体必须至少定义两个注解:@Entity@Id。 以前,我们将ddl-auto选项设置为create-drop,这意味着 Hibernate 将根据该实体创建表模式。

@Entity
@Table(name = "countries")
public class Country {

@Entity注解指定该类是一个实体,并映射到数据库表。 @Table注解指定要用于映射的数据库表的名称。

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Id注解指定实体的主键,@GeneratedValue给出主键值的生成策略。

com/zetcode/repository/CountryRepository.java

package com.zetcode.repository;

import com.zetcode.bean.Country;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface CountryRepository extends CrudRepository<Country, Long> {

}

CountryRepository@Repository注解修饰。

通过从 Spring CrudRepository扩展,我们为数据库实现了一些方法,包括findAll()。 这样可以节省一些样板代码。

com/zetcode/service/ICountryService.java

package com.zetcode.service;

import com.zetcode.bean.Country;
import java.util.List;

public interface ICountryService {

    public List<Country> findAll();
}

ICountryService包含findAll()契约方法。

com/zetcode/service/CountryService.java

package com.zetcode.service;

import com.zetcode.model.Country;
import com.zetcode.repository.CountryRepository;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class CountryService implements ICountryService {

    @Autowired
    private CountryRepository repository;

    @Override
    public List<Country> findAll() {

        var countries = (List<Country>) repository.findAll();
        return countries;
    }
}

CountryService包含findAll()方法的实现。

@Autowired
private CountryRepository repository;

CountryRepository注入了@Autowired注解。

@Override
public List<Country> findAll() {

    var countries = (List<Country>) repository.findAll();
    return countries;
}

findAll()方法从数据库返回所有国家的列表。

com/zetcode/controller/MyController.java

package com.zetcode.controller;

import com.zetcode.model.Country;
import com.zetcode.service.ICountryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.HashMap;
import java.util.List;

@Controller
public class MyController {

    @Autowired
    ICountryService countryService;

    @RequestMapping("/showCountries")
    public ModelAndView findCities() {

        var countries = (List<Country>) countryService.findAll();

        var params = new HashMap<String, Object>();
        params.put("countries", countries);

        return new ModelAndView("showCountries", params);
    }
}

MyController处理来自客户端的请求。

@Controller
public class MyController {

控制器带有@Controller注解。

@Autowired
ICountryService countryService;

ICountryService注入到countryService字段中。

var countries = (List<Country>) countryService.findAll();

从服务对象中,我们使用findAll()方法检索所有国家。

var params = new HashMap<String, Object>();
params.put("countries", countries);

return new ModelAndView("showCountries", params);

处理和国家列表一起发送到showCountries.ftl模板文件。 我们已经在 Maven POM 文件中提供了 Freemarker 依赖项; 因此,Spring Boot 会找出视图的扩展名。

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="showCountries">Show countries</a>
        </p>
    </body>
</html>

这是主页。 它包含一个获取所有国家的链接。

resources/templates/showCountries.ftl

<!DOCTYPE html>
<html>
    <head>
        <title>Show countries</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        </head>

    <body>

        <h2>List of countries</h2>

        <table>
            <tr>
                <th>Id</th>
                <th>Name</th>
                <th>Population</th>
            </tr>

            <#list countries as country>
                <tr>
                    <td>${country.id}</td>
                    <td>${country.name}</td>
                    <td>${country.population}</td>
                </tr>
            </#list>
        </table>
    </body>
</html>

这是showCountries.ftl模板文件。 使用#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);
    }
}

Application是设置 Spring Boot 应用的入口。 @SpringBootApplication注解启用自动配置和组件扫描。 它是@Configuration@EnableAutoConfiguration@ComponentScan注解的便捷注解。

$ mvn -q spring-boot:run

我们运行该应用。

在本教程中,我们展示了如何在 Spring 应用中使用@Repository注解。 您可能也对相关教程感兴趣: Spring Boot @Controller教程Spring Boot @RequestParam教程Java 教程,或列出全部 Spring Boot 教程

Spring Boot MongoDB 教程

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

Spring Boot MongoDB 教程展示了如何在 Spring Boot 框架中访问 MongoDB 中的数据。

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

MongoDB

MongoDB 是 NoSQL 跨平台的面向文档的数据库。 它是可用的最受欢迎的数据库之一。 MongoDB 由 MongoDB Inc. 开发,并作为免费和开源软件发布。

Spring Data MongoDB 项目提供了与 MongoDB 文档数据库的集成。

安装 MongoDB

以下命令可用于在基于 Debian 的 Linux 上安装 MongoDB。

$ sudo apt-get install mongodb

该命令将安装 MongoDB 随附的必要包。

$ sudo service mongodb status
mongodb start/running, process 975

使用sudo service mongodb status命令,我们检查mongodb服务器的状态。

$ sudo service mongodb start
mongodb start/running, process 6448

mongodb服务器由sudo service mongodb start命令启动。

Spring Boot MongoDB 示例

在以下示例中,我们创建一个使用 MongoDB 数据库的简单 Spring Boot 应用。 请注意,默认情况下,没有任何特定配置,Spring Boot 会尝试使用test数据库名称连接到本地托管的 MongoDB 实例。

pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           │   MyRunner.java
│   │           ├───model
│   │           │       Country.java
│   │           └───repository
│   │                   CountryRepository.java
│   └───resources
│           application.properties
└───test
    └───java
        └───com
            └───zetcode
                    MongoTest.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>springbootmongodb</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-mongodb</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 pom.xml文件。

Spring Boot 启动器是一组方便的依赖项描述符,可以极大地简化 Maven 配置。 spring-boot-starter-parent具有 Spring Boot 应用的一些常用配置。 spring-boot-starter-data-mongodb是使用 MongoDB 面向文档的数据库和 Spring Data MongoDB 的入门。 spring-boot-starter-test是使用包含 JUnit,Hamcrest 和 Mockito 的库测试 Spring Boot 应用的入门程序。

spring-boot-maven-plugin提供了 Maven 的 Spring Boot 支持,使我们能够打包可执行的 JAR 或 WAR 档案。 它的spring-boot:run目标运行 Spring Boot 应用。

resources/application.properties

spring.main.banner-mode=off
logging.level.org.springframework=ERROR

application.properties中,我们打开 Spring Boot 横幅并设置日志记录属性。 默认情况下,Spring Boot 会尝试使用测试数据库连接到 MongoDB 的本地托管实例。

# mongodb
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=testdb

如果要配置 MongoDB,可以设置相应的属性。

com/zetcode/model/Country.java

package com.zetcode.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.Objects;

@Document
public class Country {

    @Id
    private String id;
    private String name;
    private int population;

    public Country(String name, int population) {
        this.name = name;
        this.population = population;
    }

    public String getId() {
        return id;
    }

    public void setId(String 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;
        Country country = (Country) o;
        return population == country.population &&
                Objects.equals(id, country.id) &&
                Objects.equals(name, country.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, population);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Country{");
        sb.append("id='").append(id).append('\'');
        sb.append(", name='").append(name).append('\'');
        sb.append(", population=").append(population);
        sb.append('}');
        return sb.toString();
    }
}

这是Country bean,具有三个属性:idnamepopulation

@Document
public class Country {

Bean 用可选的@Document注解修饰。

@Id
private String id;

id@Id注解修饰。 Spring 会自动为一个新生成的国家对象生成一个新的 ID。

com/zetcode/repository/CountryRepository.java

package com.zetcode.repository;

import com.zetcode.model.Country;
import org.springframework.data.mongodb.repository.MongoRepository;

public interface CountryRepository extends MongoRepository<Country, String> {

}

通过从MongoRepository扩展,我们可以直接使用许多操作,包括标准 CRUD 操作。

com/zetcode/MyRunner.java

package com.zetcode;

import com.zetcode.model.Country;
import com.zetcode.repository.CountryRepository;
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 CountryRepository repository;

    @Override
    public void run(String... args) throws Exception {

        repository.deleteAll();

        repository.save(new Country("China", 1382050000));
        repository.save(new Country("India", 1313210000));

        repository.findAll().forEach((country) -> {
            logger.info("{}", country);
        });
    }
}

我们有一个命令行运行器。 在其run()方法中,我们访问 MongoDB。

@Autowired
private CountryRepository repository;

CountryRepository注入了@Autowired注解。

repository.deleteAll();

如果有,我们将使用deleteAll()删除所有国家。

repository.save(new Country("China", 1382050000));

我们用save()保存一个国家。

repository.findAll().forEach((country) -> {
    logger.info("{}", country);
});

我们使用findAll()方法遍历数据库中的所有国家。

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

com/zetcode/MongoTest.java

package com.zetcode;

import com.zetcode.model.Country;
import com.zetcode.repository.CountryRepository;
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.data.domain.Example;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.Optional;

import static junit.framework.TestCase.assertEquals;
import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MongoTest {

    @Autowired
    private CountryRepository repository;

    private static final int NUMBER_OF_COUNTRIES = 6;

    @Before
    public void init() {

        repository.deleteAll();

        repository.save(new Country("China", 1382050000));
        repository.save(new Country("India", 1313210000));
        repository.save(new Country("USA", 324666000));
        repository.save(new Country("Indonesia", 260581000));
        repository.save(new Country("Brazil", 207221000));
        repository.save(new Country("Pakistan", 196626000));
    }

    @Test
    public void countAllCountries() {

        var countries = repository.findAll();
        assertEquals(NUMBER_OF_COUNTRIES, countries.size());
    }

    @Test
    public void countOneCountry() {

        Example<Country> example = Example.of(new Country("China", 1382050000));

        assertThat(repository.count(example)).isEqualTo(1L);
    }

    @Test
    public void setsIdOnSave() {

        Country nigeria = repository.save(new Country("Nigeria", 186988000));
        assertThat(nigeria.getId()).isNotNull();
    }

    @Test
    public void findOneCountry() {

        Example<Country> example = Example.of(new Country("India", 1313210000));

        Optional<Country> country = repository.findOne(example);
        assertThat(country.get().getName()).isEqualTo("India");
    }
}

我们有四种测试方法。

@Before
public void init() {

    repository.deleteAll();

    repository.save(new Country("China", 1382050000));
    repository.save(new Country("India", 1313210000));
    repository.save(new Country("USA", 324666000));
    repository.save(new Country("Indonesia", 260581000));
    repository.save(new Country("Brazil", 207221000));
    repository.save(new Country("Pakistan", 196626000));
}

init()方法中,我们保存了六个国家。

@Test
public void countAllCountries() {

    var countries = repository.findAll();
    assertEquals(NUMBER_OF_COUNTRIES, countries.size());
}

我们测试数据库中有六个国家。

@Test
public void countOneCountry() {

    Example<Country> example = Example.of(new Country("China", 1382050000));

    assertThat(repository.count(example)).isEqualTo(1L);
}

此方法测试数据库中只有一个中国。

@Test
public void setsIdOnSave() {

    Country nigeria = repository.save(new Country("Nigeria", 186988000));
    assertThat(nigeria.getId()).isNotNull();
}

我们测试了在保存新国家/地区时,自动生成的 ID 不等于null

@Test
public void findOneCountry() {

    Example<Country> example = Example.of(new Country("India", 1313210000));

    Optional<Country> country = repository.findOne(example);
    assertThat(country.get().getName()).isEqualTo("India");
}

我们测试findOne()方法找到一个国家,即印度。

在本教程中,我们学习了如何在 Spring Boot 应用中使用 MongoDB。 您可能也对相关教程感兴趣: Spring Boot REST 数据 JPA 教程Spring Boot REST H2 教程MongoDB Java 教程Java 教程 或列出所有 Spring Boot 教程

Spring Boot MongoDB 反应式教程

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

Spring Boot MongoDB 反应式教程展示了如何在 Spring Boot 应用中使用 MongoDB 进行反应式编程。

MongoDB

MongoDB 是 NoSQL 跨平台的面向文档的数据库。 它是可用的最受欢迎的数据库之一。 MongoDB 由 MongoDB Inc. 开发,并作为免费和开源软件发布。

Spring Data MongoDB 项目提供了与 MongoDB 文档数据库的集成。

反应式编程

反应式编程是一种编程范例,它是函数式的,基于事件的,非阻塞的,异步的,并且以数据流处理为中心。 术语反应式来自以下事实:我们对诸如鼠标单击或 I/O 事件之类的更改做出反应。

当我们处理大量流数据时,响应式应用可以更好地扩展,并且效率更高。 反应性应用是非阻塞的; 他们没有使用资源等待流程完成。

在构建反应式应用时,我们需要它一直到整个数据库都是反应式的。 我们需要使用支持反应式编程的数据库。 MongoDB 是具有响应式支持的数据库。

响应式应用实现基于事件的模型,在该模型中将数据推送到使用者。 数据的使用者称为订阅者,因为它订阅了发布者,后者发布异步数据流。

Spring 反应式

Spring 反应式是一个反应式库,用于根据反应式流规范在 JVM 上构建非阻塞应用。

反应式项目提供两种类型的发布者:MonoFluxFlux是产生 0 到 N 个值的发布者。 返回多个元素的操作使用此类型。 Mono是产生 0 到 1 值的发布者。 它用于返回单个元素的操作。

Spring Boot MongoDB 反应式示例

在以下应用中,我们对 MongoDB 数据库使用反应式编程。

注意:默认情况下,没有任何特定配置,Spring Boot 会尝试使用test数据库名称连接到本地托管的 MongoDB 实例。

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
└───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>springbootmongodbreactive</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-mongodb-reactive</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-data-mongodb-reactive是用于使用 MongoDB 面向文档的数据库和 Spring Data MongoDB 反应式的 Spring Boot 入门程序。

resources/application.properties

spring.main.banner-mode=off

application.properties中,我们关闭 Spring Boot 横幅并设置日志记录属性。 默认情况下,Spring Boot 会尝试使用测试数据库连接到 MongoDB 的本地托管实例。

# mongodb
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=testdb

如果要配置 MongoDB,可以设置相应的属性。

com/zetcode/model/City.java

package com.zetcode.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.Objects;

@Document(value="cities")
public class City {

    @Id
    private String id;

    private String name;
    private int population;

    public City() {
    }

    public City(String name, int population) {
        this.name = name;
        this.population = population;
    }

    public String getId() {
        return id;
    }

    public void setId(String 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 bean,具有三个属性:idnamepopulation

@Document(value="cities")
public class City {

Bean 用可选的@Document注解修饰。

@Id
private String id;

id@Id注解修饰。 Spring 会为一个新生成的城市对象自动生成一个新的 ID。

com/zetcode/repository/CityRepository.java

package com.zetcode.repository;

import com.zetcode.model.City;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;

@Configuration
public interface CityRepository extends ReactiveMongoRepository<City, String> {
}

通过从ReactiveMongoRepository扩展,我们有了一个反应性 MongoDB 存储库。

com/zetcode/service/ICityService.java

package com.zetcode.service;

import com.zetcode.model.City;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

public interface ICityService {

    Mono<City> insert(City city);
    Flux<City> saveAll(List<City> cities);
    Mono<City> findById(String id);
    Flux<City> findAll();
    Mono<Void> deleteAll();
}

ICityService包含五种契约方法。

com/zetcode/MyRunner.java

package com.zetcode;

import com.zetcode.model.City;
import com.zetcode.service.CityService;
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 reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

@Component
public class MyRunner implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);

    @Autowired
    private CityService cityService;

    @Override
    public void run(String... args) throws Exception {

        logger.info("Creating cities");

        var cities = List.of(new City("Bratislava", 432000),
                new City("Budapest", 1759000),
                new City("Prague", 1280000),
                new City("Warsaw", 1748000));

        Mono<Void> one = cityService.deleteAll();

        Flux<City> two = cityService.saveAll(cities);
        Flux<City> three = cityService.findAll();
        three.subscribe(city -> logger.info("{}", city));

        Mono<Void> all = Mono.when(one, two, three);
        all.block();
    }
}

我们有一个命令行运行器。 在其run()方法中,我们使用反应式编程访问 MongoDB。

Mono<Void> one = cityService.deleteAll();

如果集合中有城市,我们将删除所有城市。

Flux<City> two = cityService.saveAll(cities);

我们保存城市列表。

Flux<City> three = cityService.findAll();
three.subscribe(System.out::println);

我们从集合中找到所有城市。 我们使用subscribe()方法订阅发布者,并将检索到的城市打印到终端。

Mono<Void> all = Mono.when(one, two, three);

使用Mono.when(),我们将三个发布者汇总到一个新的Mono中,当所有来源完成后,这些Mono将实现。

all.block();

使用block(),我们将触发所有三个操作并等待完成。 由于我们具有控制台应用,因此我们引入了阻塞操作,以便在终端上获得结果。

subscribe()方法开始工作并立即返回。 我们不能保证在应用的其他部分运行操作完成。 block()是一项阻止操作:它触发该操作并等待其完成。

注意:通常,我们很少在应用中使用阻塞调用。 控制台应用中的操作是少数例外之一。

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

在本教程中,我们学习了如何在 Spring Boot 应用中使用反应式编程模型对 MongoDB 进行编程。

列出所有 Spring Boot 教程

Spring Boot PostgreSQL 教程

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

Spring Boot PostgreSQL 教程展示了如何在 Spring Boot 应用中使用 PostgreSQL 数据库。

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

PostgreSQL

PostgreSQL 是一个功能强大的开源对象关系数据库系统。 它是一个多用户数据库管理系统。 它可以在包括 Linux,FreeBSD,Solaris,Microsoft Windows 和 Mac OSX 在内的多个平台上运行。PostgreSQL 由 PostgreSQL 全球开发小组开发。

PostgreSQL 设置

我们将展示如何在 Debian Linux 系统上安装 PostgreSQL 数据库。

$ sudo apt-get install postgresql  

此命令将安装 PostgreSQL 服务器和相关包。

$ /etc/init.d/postgresql status

我们使用postgresql status命令检查数据库的状态。

$ sudo -u postgres psql postgres
psql (9.5.10)
Type "help" for help.

postgres=# \password postgres
Enter new password: 
Enter it again: 

安装后,将使用空的默认密码创建一个具有管理权限的postgres用户。 第一步,我们需要为postgres设置密码。

$ sudo -u postgres createuser --interactive --password user12
Shall the new role be a superuser? (y/n) n
Shall the new role be allowed to create databases? (y/n) y
Shall the new role be allowed to create more new roles? (y/n) n
Password: 

我们创建一个新的数据库用户。

$ sudo -u postgres createdb testdb -O user12

我们使用createdb命令创建一个新的testdb数据库,该数据库将由user12拥有。

$ sudo vi /etc/postgresql/9.5/main/pg_hba.conf

我们编辑pg_hba.conf文件。

# "local" is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             all             127.0.0.1/32            trust

为了能够在本地 PostgreSQL 安装中运行 Spring Boot 应用,我们将 Unix 域套接字和本地连接的认证方法更改为trust

$ sudo service postgresql restart

我们重新启动 PostgreSQL 以启用更改。

$ psql -U user12 -d testdb -W
Password for user user12: 
psql (9.5.10)
Type "help" for help.

testdb=>

现在我们可以使用psql工具连接到数据库。

Spring Boot PostgreSQL 示例

以下应用是一个简单的 Spring Boot Web 应用,它使用 PostgreSQL 数据库。 我们有一个主页,带有一个链接,用于显示数据库表中的数据。 我们使用 Thymeleaf 模板系统将数据与 HTML 连接。

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-postgres.sql
│       │   schema-postgres.sql
│       ├───static
│       │       index.html
│       └───templates
│               showCities.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>springbootpostgreex</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.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</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>

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-starter-data-jpa是将 Spring Data JPA 与 Hibernate 结合使用的入门工具。

postgresql依赖项适用于 PostgreSQL 数据库驱动程序。

spring-boot-maven-plugin提供了 Maven 的 Spring Boot 支持,使我们能够打包可执行的 JAR 或 WAR 档案。 它的spring-boot:run目标运行 Spring Boot 应用。

resources/application.properties

spring.main.banner-mode=off
logging.level.org.springframework=ERROR

spring.jpa.hibernate.ddl-auto=none

spring.datasource.initialization-mode=always
spring.datasource.platform=postgres
spring.datasource.url=jdbc:postgresql://localhost:5432/testdb
spring.datasource.username=postgres
spring.datasource.password=s$cret

spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true

application.properties文件中,我们编写了 Spring Boot 应用的各种配置设置。

使用spring.main.banner-mode属性,我们可以关闭 Spring 横幅。 要加载未嵌入的数据库,在 Spring Boot 2 中,我们需要添加spring.datasource.initialization-mode=always。 为避免冲突,我们使用spring.jpa.hibernate.ddl-auto=none关闭自动模式创建。

在 spring 数据源属性中,我们设置了 PostgreSQL 数据源。

设置spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation选项可以避免最近发生的问题。 没有此选项,我们将收到以下错误:

java.sql.SQLFeatureNotSupportedException: Method org.postgresql.jdbc.PgConnection.createClob() 
is not yet implemented.

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(Long id, String name, int population) {

        this.id = id;
        this.name = name;
        this.population = population;
    }

    public Long getId() {
        return 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() {
        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实体。 每个实体必须至少定义两个注解:@Entity@Id

@Entity
@Table(name = "cities")
public class City {

@Entity注解指定该类是一个实体,并映射到数据库表。 @Table注解指定要用于映射的数据库表的名称。

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Id注解指定实体的主键,@GeneratedValue提供规范主键值的生成策略。

resources/schema-postgres.sql

DROP TABLE IF EXISTS cities;
CREATE TABLE cities(id serial PRIMARY KEY, name VARCHAR(255), population integer);

启动应用后,如果关闭了自动模式创建,则将执行schema-postgres.sql脚本。 该脚本将创建一个新的数据库表。

resources/data-postgres.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);

之后,执行data-postgres.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()。 这样,我们节省了大量样板代码。

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

        var cities = (List<City>) repository.findAll();

        return cities;
    }
}

CityService包含findAll()方法的实现。 我们使用存储库从数据库检索数据。

@Autowired
private CityRepository repository;

注入CityRepository

var cities = (List<City>) repository.findAll();

存储库的findAll()方法返回城市列表。

com/zetcode/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.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.List;

@Controller
public class MyController {

    @Autowired
    private ICityService cityService;

    @GetMapping("/showCities")
    public String findCities(Model model) {

        var cities = (List<City>) cityService.findAll();

        model.addAttribute("cities", cities);

        return "showCities";
    }
}

MyController包含一个映射。

@Autowired
private ICityService cityService;

我们在countryService字段中插入ICityService

@GetMapping("/showCities")
public String findCities(Model model) {

    var cities = (List<City>) cityService.findAll();

    model.addAttribute("cities", cities);

    return "showCities";
}

我们将带有showCities路径的请求映射到控制器的findCities()方法。 默认请求是 GET 请求。 该模型将获取城市列表,并将处理过程发送到showCities.html Thymeleaf 模板文件。

resources/templates/showCities.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <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>

            <tr th:each="city : ${cities}">
                <td th:text="${city.id}">Id</td>
                <td th:text="${city.name}">Name</td>
                <td th:text="${city.population}">Population</td>
            </tr>
        </table>

    </body>
</html>

showCities.html模板文件中,我们在 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 cities</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 应用中使用 PostgreSQL 数据库。 您可能也对相关教程感兴趣:

查找所有 Spring Boot 教程

Spring Boot @ModelAttribute

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

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

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

@ModelAttribute

@ModelAttribute将方法参数或方法返回值绑定到已公开的 Web 视图的命名模型属性。 用@ModelAttribute注解的方法在使用@RequestMapping的控制器方法之前被调用。

Spring Boot @ModelAttribute示例

以下应用演示了@ModelAttribute的用法。 它用于在应用中生成当天的消息。 该消息是从属性文件中读取的。

pom.xml
src
├── main
│   ├── java
│   │   └── com
│   │       └── zetcode
│   │           ├── Application.java
│   │           ├── controller
│   │           │   └── MyController.java
│   │           └── service
│   │               ├── IMessageService.java
│   │               └── MessageService.java
│   └── resources
│       ├── application.properties
│       ├── static
│       │   └── index.html
│       └── templates
│           ├── pageOne.html
│           └── pageTwo.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>springbootmodelattributeex</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 pom.xml文件。 spring-boot-starter-parent是父 POM,它为使用 Maven 构建的应用提供依赖关系和插件管理。 spring-boot-starter-thymeleaf是使用 Thymeleaf 视图构建 MVC Web 应用的入门工具。 spring-boot-maven-plugin将 Spring 应用打包到可执行的 JAR 或 WAR 归档文件中。

resources/application.properties

spring.main.banner-mode=off
logging.level.org.springframework=ERROR

messages.motd=Welcome

application.properties是 Spring Boot 中的主要配置文件。 我们通过选择错误消息来关闭 Spring 横幅,并减少 Spring 框架的日志记录量。

messages.motd属性包含该消息。

com/zetcode/service/IMessageService.java

package com.zetcode.service;

public interface IMessageService {

    String getMessage();
}

IMessageService包含getMessage()合约方法。

com/zetcode/service/MessageService.java

package com.zetcode.service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class MessageService implements IMessageService {

    @Value("${messages.motd}")
    private String motd="Hello";

    @Override
    public String getMessage() {

        return motd;
    }
}

getMessage()方法的实现使用@Value注解从属性文件中检索消息。

com/zetcode/controller/MyController.java

package com.zetcode.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import com.zetcode.service.IMessageService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;

@Controller
public class MyController {

    @Autowired
    private IMessageService messageService;

    @GetMapping("/pageOne")
    public String getPageOne() {

        return "pageOne";
    }

    @GetMapping("/pageTwo")
    public String getPageTwo() {

        return "pageTwo";
    }

    @ModelAttribute("motd")
    public String message() {

        return messageService.getMessage();
    }
}

由于MyController带有@Controller注解,因此它成为 Spring MVC 控制器类。 使用@GetMapping注解,我们将两个 URL 模式映射到 Thymeleaf 视图。 这两个模板都接收motd模型属性。

@ModelAttribute("motd")
public String message() {

    return messageService.getMessage();
}

@RequestMapping方法及其特化(例如@GetMapping)之前,执行带有@ModelAttribute注解的方法。 从messageService生成的消息存储在motd模型属性中,并且可用于两个 Thymeleaf 视图。

resources/templates/pageOne.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Page one</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h2>Page one</h2>

<p th:text="'Message of the day: ' + ${motd}"></p>

</body>
</html>

这是pageOne.html视图。 使用${}语法访问motd属性。

resources/templates/pageTwo.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Page two</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>

<h2>Page two</h2>

<p th:text="'Message of the day:' + ${motd}"></p>

</body>
</html>

这是pageTwo.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="pageOne">Go to page one</a><br>
        <a href="pageTwo">Go to page two</a>
    </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注解启用自动配置和组件扫描。 它是@Configuration@EnableAutoConfiguration@ComponentScan注解的便捷注解。

$ mvn spring-boot:run

应用运行后,我们可以导航到localhost:8080

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

{% raw %}

Spring Boot CORS 教程

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

Spring Boot CORS 教程显示了如何在 Spring Boot 应用中设置跨域资源共享。

CORS

跨域资源共享(CORS)是一种安全策略,它使用 HTTP 标头来告诉浏览器,让运行在一个来源(域)上的 Web 应用有权访问来自另一个来源的服务器中的选定资源。

网页可以嵌入跨域图像,样式表,脚本,iframe和视频。 默认情况下,同源安全策略禁止某些跨域请求,尤其是 Ajax 请求。

XMLHttpRequest和 Fetch API 遵循同源策略。 因此; 使用这些 API 的 Web 应用只能从加载应用的相同来源请求 HTTP 资源,除非来自其他来源的响应包括正确的 CORS 标头。

Spring Boot CORS 示例

以下 Spring Boot 应用将 Angular 用作前端。 Angular SPA 在localhost:4200上运行,并向在localhost:8080上运行的 Spring Boot 后端发出请求。 为此,我们需要在 Spring Boot 应用中启用 CORS。

Spring Boot 后端

后端将在 Spring Boot 中创建。

pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           │   MyRunner.java
│   │           ├───config
│   │           │       AppConf.java
│   │           ├───controller
│   │           │       MyController.java
│   │           ├───model
│   │           │       City.java
│   │           └───repository
│   │                   CityRepository.java
│   └───resources
│       │   application.properties
│       └───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>corsex</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.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 构建文件。

resources/application.properties

spring.main.banner-mode=off

application.properties是主要的 Spring Boot 配置文件。 使用spring.main.banner-mode属性,我们可以关闭 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.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface CityRepository extends JpaRepository<City, Long> {

}

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

com/zetcode/controller/MyController.java

package com.zetcode.controller;

import com.zetcode.model.City;
import com.zetcode.repository.CityRepository;
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 CityRepository cityRepository;

    @GetMapping(value = "/cities")
    public List<City> cities() {

        return cityRepository.findAll();
    }
}

MyController中,我们有一个返回所有城市的端点。

注意:在Java企业应用中,定义与存储库一起使用的服务层是一个好习惯。 为简单起见,我们跳过服务层。

com/zetcode/conf/AppConf.java

package com.zetcode.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class AppConf implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:4200")
                .allowedMethods("GET");
    }
}

使用CorsRegistry,我们启用 CORS。 我们设置允许的来源和请求方法。

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));
        cityRepository.save(new City("Los Angeles", 3971000));
        cityRepository.save(new City("New York", 8550000));
        cityRepository.save(new City("Edinburgh", 464000));
    }
}

MyRunner中,我们将数据添加到内存 H2 数据库中。

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
</p>

<script>
fetch('http://localhost:8080/cities')
    .then(res => res.json())
    .then(data => console.log('Output: ', data))
    .catch(err => console.error(err));
</script>

</body>
</html>

在主页中,我们使用 Fetch API 创建一个获取所有城市的请求。 该请求来自同一来源,因此此处不需要 CORS。

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

Angular 前端

应用的前端是使用 Angular 创建的。

$ npm i -g @angular/cli
$ ng new frontend
$ cd frontend

我们创建一个新的 Angular 应用。

src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.module.ts中,我们启用了 http 模块,该模块用于发出请求。

src/app/app.component.ts

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  constructor(private http: HttpClient) { }
  title = 'frontend';
  httpdata;

  ngOnInit() {
        this.http.get('http://localhost:8080/cities')
        .subscribe((data) => this.displaydata(data));
    }
   displaydata(data) { this.httpdata = data; }

}

ngOnInit()方法中,我们向后端创建一个 GET 请求。 数据存储在httpdata中。

src/app/app.component.html

<h2>List of cities</h2>

<ul *ngFor = "let data of httpdata">
  <li>Name : {{data.name}} Population: {{data.population}}</li>
</ul>

我们使用*ngFor指令在 HTML 列表中显示数据。

$ ng serve

我们启动 Angular 服务器。

$ mvn spring-boot:run

我们运行后端服务器。 现在我们找到localhost:4200。 加载页面后,会将请求发送到 Spring Boot 应用以获取城市列表。

在本教程中,我们为具有 Angular 前端的 Spring Boot 应用启用了 CORS 支持。 由于这两个部分在不同的域上运行,因此需要 CORS。

列出所有 Spring Boot 教程

{% endraw %}

Spring Boot 提交表单教程

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

SpringBoot 提交表单教程展示了如何在 Spring Boot 应用中提交表单。

Spring 是流行的 Java 应用框架。 Spring Boot 致力于以最小的努力创建独立的,基于生产级别的基于 Spring 的应用。

Spring Boot 提交表单示例

以下应用包含一个简单的表格。 来自表单的数据会自动插入到 UI Bean 中,并且可用于视图。 Thymeleaf 用作视图引擎。

pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           ├───bean
│   │           │       User.java
│   │           └───controller
│   │                   MyController.java
│   └───resources
│       └───templates
│               addUser.html
│               showMessage.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>SpringBootSubmitFormEx</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>

        <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 在pom.xml中找到依赖项时,它将自动为我们配置 Thymeleaf。

com/zetcode/bean/User.java

package com.zetcode.bean;

public class User {

    private String name;
    private String occupation;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getOccupation() {
        return occupation;
    }

    public void setOccupation(String occupation) {
        this.occupation = occupation;
    }
}

这是User bean。 它会自动填充表单请求中的数据。 这些属性必须与表单字段匹配。

com/zetcode/controller/MyController.java

package com.zetcode.controller;

import com.zetcode.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class MyController {

    @GetMapping("/addUser")
    public String sendForm(User user) {

        return "addUser";
    }

    @PostMapping("/addUser")
    public String processForm(User user) {

        return "showMessage";
    }
}

控制器类发送和读取表单视图。

@PostMapping("/addUser")
public String processForm(User user) {

    return "showMessage";
}

User bean 作为参数传递给processForm()处理器。 Spring 尝试用请求数据填充 Bean。 数据也可自动用于 Thymeleaf showMessage视图。

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

resources/templates/addUser.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Add user</title>
    <meta charset="UTF-8">
</head>
<body>

<h1>Add User</h1>

<form action="#" th:action="@{/addUser}" th:object="${user}" method="post">
    <p>
        Name: <input type="text" th:field="*{name}">
    </p>
    <p>
        Occupation: <input type="text" th:field="*{occupation}">
    </p>
    <p>
        <input type="submit" value="Submit"/> <input type="reset" value="Reset">
    </p>
</form>

</body>
</html>

该视图包含表单。

<form action="#" th:action="@{/addUser}" th:object="${user}" method="post">

th:object引用user表单 bean。 这不是一个类名,而是一个 Spring bean 名称。 因此它是小写的。

<p>
    Name: <input type="text" th:field="*{name}">
</p>

使用*{}语法,我们引用已定义的对象。

resources/templates/showMessage.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Show message</title>
    <meta charset="UTF-8">
</head>
<body>

<h1>Result</h1>

<p th:text="'Name: ' + ${user.name}"></p>
<p th:text="'Occupation: ' + ${user.occupation}"></p>
<a href="/addUser">Submit another message</a>

</body>
</html>

该模板显示在表单中输入的数据。

<p th:text="'Name: ' + ${user.name}"></p>

我们使用${}语法引用表单 bean 属性。

导航至localhost:8080/addUser以测试应用。

在本教程中,我们展示了如何在 Spring Boot 应用中提交简单表单。 您可能也对相关教程感兴趣: Spring Boot 第一个 Web 应用Spring Boot RESTFul 应用Spring Boot @Controller教程独立 Spring 应用Java 教程

Spring Boot Model

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

Spring Boot Model教程展示了如何在 Spring Boot 应用中使用模型。 在 Spring,模型由ModelModelMapModelAndView表示。

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

MVC

MVC(模型-视图-控制器)是一种软件架构模式,它将应用分为三个部分:模型,视图和控制器。 该模型表示一个携带数据的 Java 对象。 该视图使模型包含的数据可视化。 控制器管理流入模型对象的数据流,并在数据更改时更新视图。 它使视图和模型分离。

Spring MVC

Spring MVC 是基于 Servlet API 构建的原始 Web 框架。 它基于 MVC 设计模式。 Spring Framework 5.0 引入了一个名为 Spring WebFlux 的并行反应式栈 Web 框架。

ModelModelMapModelAndView

ModelModelMapModelAndView用于在 Spring MVC 应用中定义模型。 Model定义了模型属性的持有者,并且主要用于向模型添加属性。 ModelMapModel的扩展,具有在映射和链式方法调用中存储属性的能力。 ModelAndView是模型和视图的支架; 它允许在一个返回值中返回模型和视图。

Spring Boot Model示例

以下简单的 Web 应用在控制器方法中使用ModelModelMapModelAndView。 该模型保存应用数据,该数据显示在视图中。 我们将 Freemaker 库用于视图层。

pom.xml
src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           ├── Application.java
    │   │           └── controller
    │   │               └── MyController.java
    │   └── resources
    │       ├── application.properties
    │       ├── static
    │       │   └── index.html
    │       └── templates
    │           └── show.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>springbootmodelex</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-parent是父 POM,它为使用 Maven 构建的应用提供依赖关系和插件管理。 spring-boot-starter-freemarker是 Freemarker 模板引擎的依赖项。 此依赖项还将在项目中包含 Spring MVC。 spring-boot-maven-plugin将 Spring 应用打包到可执行的 JAR 或 WAR 归档文件中。

resources/application.properties

spring.main.banner-mode=off
logging.level.org.springframework=ERROR

mymessage=Hello there

application.properties是 Spring Boot 中的主要配置文件。 我们通过选择错误消息来关闭 Spring 横幅,并减少 Spring 框架的日志记录量。 mymessage属性包含该消息。

com/zetcode/MyController.java

package com.zetcode.controller;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MyController {

    @Value("${mymessage}")
    private String message;

    @GetMapping("/getMessage")
    public String getMessage(Model model) {

        model.addAttribute("message", message);

        return "show";
    }

    @GetMapping("/getMessage2")
    public ModelAndView getMessage() {

        var mav = new ModelAndView();

        mav.addObject("message", message);
        mav.setViewName("show");

        return mav;
    }

    @GetMapping("/getMessageAndTime")
    public String getMessageAndTime(ModelMap map) {

        var ldt = LocalDateTime.now();

        var fmt = DateTimeFormatter.ofLocalizedDateTime(
                FormatStyle.MEDIUM);

        fmt.withLocale(new Locale("sk", "SK"));
        fmt.withZone(ZoneId.of("CET"));

        var time = fmt.format(ldt);

        map.addAttribute("message", message).addAttribute("time", time);

        return "show";
    }
}

这是MyController。 它具有三种响应客户端请求的方法。

@Controller
public class MyController {

MyController带有@Controller注解。

@Value("${mymessage}")
private String message;

使用@Value注解,我们将application.properties文件中的mymessage属性插入到message属性中。

@GetMapping("/getMessage")
public String getMessage(Model model) {

    model.addAttribute("message", message);

    return "show";
}

@GetMapping/getMessage URL 模式映射到getMessage()方法。 在getMessage()方法中,我们使用Model。 它使用addAttribute()方法接收message属性。 return 关键字返回视图的名称,该名称将解析为show.ftl,因为我们使用的是 Freemarker 模板系统。

@GetMapping("/getMessage2")
public ModelAndView getMessage() {

    var mav = new ModelAndView();
    mav.addObject("message", message);
    mav.setViewName("show");

    return mav;
}

在第二种情况下,我们使用ModelAndView。 我们使用addObject()setViewName()添加模型数据和视图名称。 该方法返回ModelAndView对象。

@GetMapping("/getMessageAndTime")
public String getMessageAndTime(ModelMap map) {

    var ldt = LocalDateTime.now();

    var fmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);

    fmt.withLocale(new Locale("sk", "SK"));
    fmt.withZone(ZoneId.of("CET"));

    var time = fmt.format(ldt);

    map.addAttribute("message", message).addAttribute("time", time);

    return "show";
}

getMessageAndTime()方法中,我们使用ModelMap。 模型图接收两个属性:消息和时间。

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>
        <ul>
            <li><a href="getMessage">Get message</a></li>
            <li><a href="getMessage2">Get message 2</a></li>
            <li><a href="getMessageAndTime">Get message and time</a></li>
        </ul>
    </body>
</html>

这是主页。 它包含三个调用 Spring 控制器方法的链接。 它是静态资源,位于预定义的src/main/resources/static目录中。

resources/templates/show.ftl

<!DOCTYPE html>
<html>
    <head>
        <title>Message</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <p>
            Message: ${message}
        </p>

        <#if time??>
            <p>Date and time: ${time}</p>
        </#if>

    </body>
</html>

show.ftl是一个 Freemarker 模板文件。 它位于预定义的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 应用的入口。 @SpringBootApplication注解启用自动配置和组件扫描。 在扫描过程中,将查找@Controller注解,并从MyController类创建一个 Spring bean。

$ mvn spring-boot:run

启动应用后,我们导航到localhost:8080

在本教程中,我们已经在 Spring 应用中使用了模型。 您可能也对相关教程感兴趣: Spring Boot @ModelAttribute教程Spring Boot @Controller教程Spring Boot Freemaker 教程Spring Boot @RequestParam教程Java 教程或列出所有 Spring Boot 教程

Spring Boot MySQL 教程

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

Spring Boot MySQL 教程展示了如何在 Spring Boot 应用中使用 MySQL 数据库。

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

MySQL

MySQL 是领先的开源数据库管理系统。 它是一个多用户,多线程的数据库管理系统。 MySQL 在网络上特别流行。 它是非常流行的 LAMP 平台的一部分,该平台包括 Linux,Apache,MySQL 和 PHP。 MySQL 数据库在最重要的 OS 平台上可用。 它可以在 BSD Unix,Linux,Windows 和 Mac 上运行。

MySQL 设置

我们将展示如何在 Debian Linux 系统上安装 MySQL 数据库。

$ sudo apt-get install mysql-server

此命令将安装 MySQL 服务器和相关包。

$ sudo service mysql start
$ sudo service mysql stop

这两个命令用于启动和停止 MySQL。

$ sudo service mysql status

我们使用service mysql status命令检查数据库的状态。

$ mysql -u root -p

现在我们需要重置根密码。 我们启动 mysql 命令行工具。 (服务器必须正在运行。)我们以root用户身份连接。

mysql> SET PASSWORD = PASSWORD('newpassowrd');

我们为root设置了新密码。

$ mysql_secure_installation

我们可以使用mysql_secure_installation来提高 MySQL 服务器的安全性。 我们可以选择改进 MySQL 的root密码,删除匿名用户帐户,禁用 localhost 外部的root登录以及删除测试数据库。

mysql> CREATE DATABASE testdb;

我们创建一个新的testdb数据库。

mysql> CREATE USER user12@localhost IDENTIFIED BY 's$cret';
mysql> GRANT ALL ON testdb.* TO user12@localhost;

我们创建一个新的 MySQL 用户并将其特权授予testdb数据库。

创建 MySQL 表

现在,我们将创建一个名为cities的新 MySQL 表。

cities_mysql.sql

DROP TABLE IF EXISTS cities;
CREATE TABLE cities(id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255),
    population INT);

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

这是用于创建cities表的 SQL。

mysql> use testdb;
mysql> source cities_mysql.sql

使用source命令,执行 SQL 语句。

Spring Boot MySQL 示例

以下应用是一个简单的 Spring Boot Web 应用,它使用 MySQL 数据库。 我们有一个主页,带有一个链接,用于显示数据库表中的数据。 我们使用 Freemarker 模板系统将数据与 HTML 连接。

pom.xml
src
└── main
    ├── java
    │   └── com
    │       └── zetcode
    │           ├── Application.java
    │           ├── model
    │           │   └── City.java
    │           ├── controller
    │           │   └── MyController.java
    │           ├── repository
    │           │   └── CityRepository.java
    │           └── service
    │               ├── CityService.java
    │               └── ICityService.java
    └── resources
        ├── application.properties
        ├── static
        │   └── index.html
        └── templates
            └── showCities.ftl

这是项目结构。

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>springbootmysqlex</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>mysql</groupId>
            <artifactId>mysql-connector-java</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-data-jpa</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)应用的入门工具。 spring-boot-starter-freemarker是使用 Freemarker 视图构建 MVC Web 应用的入门工具。 spring-boot-starter-data-jpa是将 Spring Data JPA 与 Hibernate 结合使用的入门工具。

mysql-connector-java依赖项是针对 MySQL 数据库驱动程序的。

spring-boot-maven-plugin在 Maven 中提供了 Spring Boot 支持,使我们可以打包可执行的 JAR 或 WAR 档案。 它的spring-boot:run目标运行 Spring Boot 应用。

resources/application.properties

spring.main.banner-mode=off
logging.level.org.springframework=ERROR

spring.datasource.url=jdbc:mysql://localhost:3306/testdb?useSSL=false
spring.datasource.username=user12
spring.datasource.password=s$cret

application.properties文件中,我们编写了 Spring Boot 应用的各种配置设置。 使用spring.main.banner-mode属性,我们可以关闭 Spring 横幅。 使用logging.level.org.springframework,我们将 spring 框架的日志记录级别设置为ERROR。 在 spring 数据源属性中,我们设置了 MySQL 数据源。

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(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 = 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() {
        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实体。 每个实体必须至少定义两个注解:@Entity@Id

@Entity
@Table(name = "cities")
public class City {

@Entity注解指定该类是一个实体,并映射到数据库表,而@Table注解指定要用于映射的数据库表的名称。

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

实体的主键由@Id注解指定。 @GeneratedValue提供了一种用于生成主键值的策略。

com/zetcode/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()。 这样,我们节省了大量样板代码。

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提供findAll()契约方法声明,以从数据源获取所有城市。

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/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 findCities(Model model) {

        var cities = (List<City>) cityService.findAll();

        model.addAttribute("cities", cities);

        return "showCities";
    }
}

MyController类用@Controller注解。

@Autowired
private ICityService cityService;

我们将ICityService注入countryService字段。

@GetMapping("/showCities")
public String findCities(Model model) {

    var cities = (List<City>) cityService.findAll();

    model.addAttribute("cities", cities);

    return "showCities";
}

我们将带有/showCities路径的请求映射到控制器的findCities()方法。 @GetMapping注解将 GET 请求映射到该方法。 该模型将获取城市列表,并将处理过程发送到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="showCities">Show cities</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 应用中使用 MySQL 数据库。 您可能也对相关教程感兴趣:

查找所有 Spring Boot 教程

Spring Boot GenericApplicationContext

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

Spring Boot GenericApplicationContext教程展示了如何在 Spring 应用中使用GenericApplicationContext。 在示例中,我们创建一个 Spring Boot 控制台应用。

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

GenericApplicationContext

GenericApplicationContextApplicationContext的实现,它不采用特定的 bean 定义格式; 例如 XML 或注解。

Spring Boot GenericApplicationContext示例

在以下应用中,我们创建一个GenericApplicationContext,并使用上下文的registerBean()方法注册一个新 bean。 稍后,我们使用getBean()从应用上下文中检索 bean。

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>genappctx</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>genappctx</name>
    <description>Using GenericApplicationContext</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</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 pom.xml文件。 spring-boot-starter-parent是父 POM,它为使用 Maven 构建的应用提供依赖关系和插件管理。 spring-boot-starter是一个核心启动器,包括自动配置支持,日志记录和 YAML。 spring-boot-starter-test在 Spring 中增加了测试支持。 spring-boot-maven-plugin将 Spring 应用打包到可执行的 JAR 或 WAR 归档文件中。

application.properties

spring.main.banner-mode=off
logging.level.root=ERROR
logging.pattern.console=%d{dd-MM-yyyy HH:mm:ss} %magenta([%thread]) %highlight(%-5level) %logger.%M - %msg%n

application.properties是 Spring Boot 中的主要配置文件。 我们关闭 Spring 横幅,减少仅记录错误的错误,并设置控制台记录模式。

TimeService.java

package com.zetcode.service;

import java.time.Instant;

public class TimeService {

    public Instant getNow() {

        return Instant.now();
    }
}

TimeService包含一个返回当前日期和时间的简单方法。 该服务类将在我们的通用应用上下文中注册。

MyApplication.java

package com.zetcode;

import com.zetcode.service.TimeService;
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.context.support.GenericApplicationContext;

@SpringBootApplication
public class MyApplication implements CommandLineRunner {

    @Autowired
    private GenericApplicationContext context;

    public static void main(String[] args) {

        SpringApplication.run(MyApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {

        context.registerBean("com.zetcode.Service.TimeService",
                TimeService.class, () -> new TimeService());

        var timeService = (TimeService) context.getBean(TimeService.class);

        System.out.println(timeService.getNow());

        context.registerShutdownHook();
    }
}

MyApplication是设置 Spring Boot 应用的入口。 @SpringBootApplication注解启用自动配置和组件扫描。 它是@Configuration@EnableAutoConfiguration@ComponentScan注解的便捷注解。

@Autowired
private GenericApplicationContext context;

我们注入GenericApplicationContext

context.registerBean("com.zetcode.Service.TimeService",
    TimeService.class, () -> new TimeService());

使用registerBean()方法注册了一个新的TimeService bean。

var timeService = (TimeService) context.getBean(TimeService.class);

我们使用getBean()检索 bean。

System.out.println(timeService.getNow());

最后,我们调用 bean 的getNow()方法。

MyApplicationTests.java

package com.zetcode;

import com.zetcode.service.TimeService;
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.context.support.GenericApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;

import java.time.Instant;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyApplicationTests {

    @Autowired
    private GenericApplicationContext context;

    @Test
    public void testNow() {

        var timeService = (TimeService) context.getBean("com.zetcode.Service.TimeService");
        var now = timeService.getNow();

        assertThat(now.isBefore(Instant.now()));
    }
}

我们有一个使用TimeServicegetNow()方法的简单测试。

var timeService = (TimeService) context.getBean("com.zetcode.Service.TimeService");

这次,我们通过给定名称引用 Bean。

$ mvn -q spring-boot:run
2018-11-24T16:31:32.146393700Z

我们运行该应用。

在本教程中,我们展示了如何在 Spring 应用中使用GenericApplicationContext。 您可能也对相关教程感兴趣: Spring Boot @PostConstruct教程Spring Boot @Controller教程Spring Boot @ExceptionHandler教程Spring Boot 上传文件Spring Boot @PathVariable教程Spring Boot @RequestParam教程Spring Boot @ResponseBody教程Java 教程

SpringApplicationBuilder教程

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

SpringApplicationBuilder教程展示了如何使用SpringApplicationBuilder创建一个简单的 Spring Boot 应用。

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

SpringApplication

SpringApplication是一个类,用于从 Java main方法引导 Spring 应用。 它创建一个适当的ApplicationContext实例(取决于类路径),注册一个CommandLinePropertySource以将命令行参数公开为 Spring 属性,刷新应用上下文,加载所有单例 bean,并触发任何CommandLineRunner bean。

SpringApplicationBuilder

SpringApplicationBuilderSpringApplicationApplicationContext实例的构建器,具有便利的流利的 API 和上下文层次结构支持。

Spring Boot 示例

以下应用是一个简单的 Spring Boot 控制台应用,它使用SpringApplicationBuilder设置 Spring Boot 应用。

该应用从用户那里获取参数。 它需要网站的完整 URL 并返回其标题。

$ tree
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           ├── Application.java
    │   │           └── MyRunner.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>SpringBootAppEx</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</artifactId>
        </dependency>        

        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.11.2</version>
        </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是核心 Spring 启动器。 jsoup依赖关系用于 JSoup 库。

spring-boot-maven-plugin提供了 Maven 的 Spring Boot 支持,使我们能够打包可执行的 JAR 或 WAR 档案。 它的spring-boot:run目标运行 Spring Boot 应用。

application.properties

logging.level.org.springframework=ERROR

application.properties文件中,我们编写了 Spring Boot 应用的各种配置设置。 在这里,我们将 Spring 框架的日志记录级别设置为ERROR

MyRunner.java

package com.zetcode;

import java.util.List;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class MyRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {

        if (!args.containsOption("website")) {

            System.err.println("no website specified");
        } else {

            List<String> vals = args.getOptionValues("website");
            String url = vals.get(0);

            Document doc = Jsoup.connect(url).get();
            String title = doc.title();
            System.out.printf("The title is: %s%n", title);
        }
    }
}

加载 Spring 应用后,将执行实现ApplicationRunner的所有 bean。

if (!args.containsOption("website")) {

我们检查命令行上是否指定了--website选项。

List<String> vals = args.getOptionValues("website");
String url = vals.get(0);

我们得到可选的值。

Document doc = Jsoup.connect(url).get();
String title = doc.title();
System.out.printf("The title is: %s%n", title);

通过JSoup,我们获得了指定网站的标题。

Application.java

package com.zetcode;

import org.springframework.boot.Banner;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {

        new SpringApplicationBuilder(Application.class)
                .bannerMode(Banner.Mode.OFF)
                .logStartupInfo(false)
                .build()
                .run(args);
    }
}

Application是设置 Spring Boot 应用的入口。 @SpringBootApplication注解启用自动配置和组件扫描。

new SpringApplicationBuilder(Application.class)
        .bannerMode(Banner.Mode.OFF)
        .logStartupInfo(false)
        .build()
        .run(args);

SpringApplicationBuilder用于构建 Spring 应用。 我们关闭横幅和启动信息。

$ mvn -q spring-boot:run -Drun.arguments=--website=http://www.something.com
The title is: Something.

这是输出。 命令行参数与run.arguments一起传递。

在本教程中,我们介绍了SpringApplicationBuilder

您可能对 JSoup 教程Spring Boot Environment教程感兴趣。 Spring Boot PostgreSQL 教程Spring Boot MySQL 教程Spring Boot @PostConstruct

Spring Boot Undertow 教程

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

Spring Boot Undertow 教程展示了如何在 Spring Boot 应用中使用 Undertow 服务器。

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

Undertow

Undertow 是一种灵活的高性能 Web 服务器,它提供阻止和非阻止 API。 它来自 JBoss 项目。

Spring Boot Undertow 示例

默认情况下,Spring Boot 使用 Tomcat 嵌入式 Web 服务器。 以下示例显示了如何使用 Undertow。

pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           ├───config
│   │           │       AppConfig.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>undertowex</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>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

这是 Maven 构建文件。 我们明确排除了 Tomcat 服务器依赖项,并包括了 Undertow 依赖项。

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 启动应用的各种配置设置。 使用spring.main.banner-mode属性,我们可以关闭 Spring 横幅。 logging.pattern.console配置控制台日志记录模式。

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 home() {

        return "Home page";
    }
}

主页返回一条简单的文本消息。

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

我们运行该应用并导航到localhost:8080

...
19-06-28 Fri 17:37:08.557 INFO  nio XNIO NIO Implementation Version 3.3.8.Final
19-06-28 Fri 17:37:08.629 INFO  UndertowServletWebServer Undertow started on port(s) 8080 (http) with context path ''
19-06-28 Fri 17:37:08.634 INFO  Application Started Application in 3.716 seconds (JVM running for 4.352)    

在控制台中,我们可以看到 Undertow 服务器正在启动。

在本教程中,我们展示了如何在 Spring Boot 应用中使用 Undertow 服务器。

列出所有 Spring Boot 教程

Spring Boot 登录页面教程

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

Spring Boot 登录页面教程显示了如何使用默认登录页面。 Spring Security 默认情况下会保护所有 HTTP 端点。 用户必须以默认的 HTTP 形式登录。

为了启用 Spring Boot 安全性,我们将spring-boot-starter-security添加到依赖项中。

Spring Boot 登录页面示例

以下示例显示了如何在 Spring Boot 应用中设置简单的登录页面。

pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           └───controller
│   │                   MyController.java
│   └───resources
│           application.properties
└───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>springbootloginpage</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-security</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

这是 Maven 构建文件。 我们有网络和安全性的入门者。

resources/application.properties

spring.main.banner-mode=off
logging.pattern.console=%d{dd-MM-yyyy HH:mm:ss} %magenta([%thread]) %highlight(%-5level) %logger.%M - %msg%n

application.properties文件中,我们关闭 Spring Boot 横幅并配置控制台日志记录模式。

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 "This is home page";
    }
}

我们有一个简单的主页。

我们运行该应用并导航到localhost:8080。 我们被重定向到http://localhost:8080/login页面。

...
17-06-2019 17:48:45 [main] INFO  org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration.getOrDeducePassword -

Using generated security password: df7ce50b-abae-43a1-abe1-0e17fd81a454
...

在控制台中,我们可以看到为默认用户user生成的密码。 这些凭据将提供给认证表单。

Login form

图:登录表单

Spring 使用 Bootstrap 定义 UI。

spring.security.user.name = admin
spring.security.user.password = s$cret

通过这两个选项,我们可以拥有一个新的用户名和密码。 使用这些设置将自动生成的用户关闭。

在本教程中,我们使用了默认的登录表单。 您可能也对相关教程感兴趣: Spring Boot @Lazy教程Java 教程或列出 Spring Boot 教程

Spring Boot RouterFunction 教程

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

Spring Boot RouterFunction教程展示了如何在 Spring Boot 应用中创建函数式路由。

反应式编程

反应式编程是一种编程范例,它是函数式的,基于事件的,非阻塞的,异步的,并且以数据流处理为中心。 术语反应式来自以下事实:我们对诸如鼠标单击或 I/O 事件之类的更改做出反应。

传统的 Spring MVC 应用使用诸如@GetMapping之类的注解将请求路径映射到控制器动作。 函数式路由 API 是此映射的替代方法。

RouterFunction

RouterFunction表示路由到处理器函数的函数。

Spring Boot RouterFunction 示例

在以下应用中,我们创建具有函数式路由的反应式 Spring Boot 应用。

pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           └───routes
│   │                   MyRoutes.java
│   └───resources
└───test
    └───java
        └───com
            └───zetcode
                └───routes
                        MyRoutesTest.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>springbootrouterfunction</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>

        <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 pom.xml文件。 RouterFunction依赖于spring-boot-starter-webflux

com/zetcode/routes/MyRoutes.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.BodyInserters.fromObject;
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 MyRoutes {

    @Bean
    RouterFunction<ServerResponse> home() {
        return route(GET("/"), request -> ok().body(fromObject("Home page")));
    }

    @Bean
    RouterFunction<ServerResponse> about() {
        return route(GET("/about"), request -> ok().body(fromObject("About page")));
    }
}

我们定义了两个函数式路由。

@Bean
RouterFunction<ServerResponse> home() {
    return route(GET("/"), request -> ok().body(fromObject("Home page")));
}

通过函数式路由,我们可以编写简单而优雅的代码。 在这里,我们返回主页的简单文本消息。

com/zetcode/routes/MyRoutesTest.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 MyRoutesTest {

    @Autowired
    private WebTestClient client;

    @Test
    public void test_home_page() {

        client.get().uri("/").exchange().expectStatus().isOk()
                .expectBody(String.class).isEqualTo("Home page");
    }

    @Test
    public void test_about_page() {

        client.get().uri("/about").exchange().expectStatus().isOk()
                .expectBody(String.class).isEqualTo("About page");
    }
}

使用WebTestClient,我们测试了两个路由。

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

在本教程中,我们学习了如何通过RouterFunction使用函数式路由。

列出所有 Spring Boot 教程。

Spring Boot @Order教程

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

Spring Boot @Order教程展示了如何使用@Order注解来规定 bean。

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

@Order

@Order定义带注解的组件的排序顺序。 value()是可选的,表示订单值。 较低的值具有较高的优先级。

Spring Boot @Order示例

以下应用命令执行CommandLineRunner的 bean 的执行。

pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │               Application.java
│   │               MyRunner.java
│   │               MyRunner2.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>springbootorder</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 pom.xml文件。

com/zetcode/MyRunner.java

package com.zetcode;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Order(value = 2)
public class MyRunner implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);

    @Override
    public void run(String... args) throws Exception {

        logger.info("Running MyRunner");
    }
}

在应用启动时启动 Bean。 使用@Order注解,我们为其赋予了优先级。

com/zetcode/MyRunner2.java

package com.zetcode;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Order(value = 1)
public class MyRunner2 implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);

    @Override
    public void run(String... args) throws Exception {
        logger.info("Running MyRunner2");
    }
}

这是MyRunner2。 通过@Order设置了更高的优先级,因此在MyRunner之前执行。

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运行该应用。

在本教程中,我们展示了如何使用@Order接口来设置 bean 的执行顺序。 您可能也对相关教程感兴趣: Spring Boot @Repository教程Spring Boot CommandLineRunner教程Java 教程或列出所有 Spring Boot 教程

Spring Boot @Lazy教程

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

Spring Boot @Lazy教程展示了如何使用 Spring @Lazy注解懒惰地初始化 bean。

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

@Lazy

@Lazy注解指示是否要延迟初始化 bean。 它可以用于@Component@Bean定义。 @Lazy bean 不会被初始化,直到被另一个 bean 引用或从BeanFactory中显式检索。 不使用@Lazy注解的 Bean 会被初始化。

Spring Boot @Lazy示例

在下面的示例中,我们创建延迟懒惰地初始化的 bean。 它说明了两种类型的 bean 之间的区别。 该应用是一个简单的 Spring Boot Web 应用,可在嵌入式 Tomcat 服务器上运行。 我们使用 Freemarker 模板引擎。

$ tree
.
├── pom.xml
├── SpringBootLazy.iml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           ├── Application.java
    │   │           ├── bean
    │   │           │   ├── MyBean.java
    │   │           │   ├── MyLazyBean.java
    │   │           │   └── StartUpBean.java
    │   │           └── controller
    │   │               └── MyController.java
    │   └── resources
    │       ├── application.properties
    │       ├── static
    │       │   └── index.html
    │       └── templates
    │           └── showMessages.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>SpringBootLazy</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.10.RELEASE</version>
    </parent>

    <dependencies>

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

Spring Boot 启动器是一组方便的依赖项描述符,可以极大地简化 Maven 配置。 spring-boot-starter-parent具有 Spring Boot 应用的一些常用配置。 spring-boot-starter-freemarker是使用 Freemarker 视图构建 MVC Web 应用的入门工具。

spring-boot-maven-plugin提供了 Maven 的 Spring Boot 支持,使我们能够打包可执行的 JAR 或 WAR 档案。 它的spring-boot:run目标运行 Spring Boot 应用。

application.properties

server.port=8086
server.contextPath=/myapp

spring.main.banner-mode=off
logging.level.org.springframework=ERROR

application.properties文件中,我们编写了 Spring Boot 应用的各种配置设置。 完成这些设置后,我们可以通过localhost:8086/myapp/访问该应用。

MyBean.java

package com.zetcode.bean;

import org.springframework.stereotype.Component;

import java.util.logging.Logger;

@Component
public class MyBean {

    static Logger log = Logger.getLogger(MyBean.class.getName());

    public MyBean() {

        log.info("MyBean initialized");
    }

    public String getMessage() {

        return "Message from MyBean";
    }
}

这是MyBean。 急切地初始化该 bean,即在 Spring 框架开始时。

MyLazyBean.java

package com.zetcode.bean;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.util.logging.Logger;

@Component
@Lazy
public class MyLazyBean {

    static Logger log = Logger.getLogger(MyLazyBean.class.getName());

    public MyLazyBean() {

        log.info("MyLazyBean initialized");
    }

    public String getMessage() {

        return "Message from MyLazyBean";
    }
}

MyLazyBean包含@Lazy注解。 第一次请求时,它会延迟地初始化。 从控制器请求。

StartUpBean.java

package com.zetcode.bean;

import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.util.logging.Logger;

@Component
public class StartUpBean implements 
        ApplicationListener<ApplicationReadyEvent> {

    static Logger log = Logger.getLogger(StartUpBean.class.getName());

    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {

        log.info("Application is ready");
    }
}

StartUpBean实现一个应用监听器; 当应用准备就绪时,它将记录一条消息。

MyController.java

package com.zetcode.controller;

import com.zetcode.bean.MyBean;
import com.zetcode.bean.MyLazyBean;
import org.springframework.beans.factory.BeanFactory;
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 BeanFactory factory;

    @GetMapping(value="messages")
    public String getMessages(Model model) {

        MyLazyBean myLazyBean = factory.getBean(MyLazyBean.class);
        MyBean myBean = factory.getBean(MyBean.class);

        model.addAttribute("mybean", myBean.getMessage());
        model.addAttribute("mylazybean", myLazyBean.getMessage());

        return "showMessages";
    }
}

这是一个控制器类。 它创建两个 bean 并接收它们的消息。 消息显示在 Freemarker 模板中。

@Autowired
private BeanFactory factory;

我们注入BeanFactory。 工厂用于访问 Spring bean。

MyLazyBean myLazyBean = factory.getBean(MyLazyBean.class);

此时,MyLazyBean被初始化。

MyBean myBean = factory.getBean(MyBean.class);

我们从工厂获得了MyBeanMyBean在 Spring 的启动时初始化。

showMessages.ftl

<!DOCTYPE html>
<html>
    <head>
        <title>Show data</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        </head>
    <body>

        <p>
            MyBean: ${mybean}
        </p>

        <p>
            MyLazyBean: ${mylazybean}
        </p>

    </body>
</html>

Freemarker 模板显示来自两个 bean 的消息。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Home page</title>
</head>
<body>

<a href="messages">Get messages</a>

</body>
</html>

index.html中有一个链接,用于从 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 

应用运行后,我们可以导航到localhost:8086/myapp/

Initializing Spring embedded WebApplicationContext
com.zetcode.bean.MyBean : MyBean initialized
com.zetcode.bean.StartUpBean : Application is ready

当 Spring 启动时,我们可以看到这些日志消息。 请注意,MyBean在启动时已初始化。

com.zetcode.bean.MyLazyBean : MyLazyBean initialized

调用控制器时,将初始化MyLazyBean

在本教程中,我们展示了如何使用 Spring @Lazy注解。 您可能也对相关教程感兴趣:

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