Spring Boot 学习(1)

文 by / 林本托html

Tip
作一个终身学习的人。java

Spring Boot

代码地址:
https://github.com/iqcz/Springbootdemomysql

Spring Boot 初体验

Spring Boot 包含了不少 start(Spring boot 中 的叫法,就是一个模块,后文统一称模块,便于理解),这些模块其实早就是 Spring Boot 你们庭的成员。这章主要介绍http://start.spring.io/,Spring Boot 提供的可用的组建,经过这个连接咱们能够快速搭建一个项目。git

这章主要包括如下内容:github

  1. 使用 Spring Boot 模板和模块;
  2. 建立一个简单的应用;
  3. 使用 Gradle 启动一个应用;
  4. 使用命令行命令;
  5. 设置数据库链接;
  6. 创建一个数据库;
  7. 计划执行。

Spring Boot 介绍

在当今软件开发快节奏的世界中,应用程序建立的速度和快速原型的需求正在变得愈来愈重要。 若是您正在使用 JVM 语言开发软件,那么 Spring Boot 就是一种可以为您提供灵活性的框架,从而使您可以以快速的速度生产高质量的软件。 因此,让咱们来看看 Spring Boot 如何帮助你实现你的应用程序。web

一. 使用 Spring Boot 模板和模块

Spring Boot 提供了超过40种不一样的模块,它们为许多不一样的框架提供即用型集成库,例如关系型和 NoSQL 的数据库链接,Web 服务,社交网络集成,监控库,日志记录,模板渲染, 并且这个名单一直在扩大。 虽然覆盖这些组件中的每个功能不是实际可行的,可是咱们将会重点介绍一些重要和受欢迎的组件,以便了解 Spring Boot 为咱们提供的可能性和易用性。spring

咱们将从建立一个基本的简单项目框架开始,Spring Boot 将帮助咱们:sql

  1. 访问连接http://start.spring.io/
  2. 填写一个简单的表单关于咱们想要的项目细节;
  3. 而后点击“Generate Project” 按钮,而后就会下载咱们预约义的项目原型。

网站的截图以下:数据库

http://start.spring.io/

在上面截图中,你会看到“Project Dependencies”部分,若是你的项目须要链接数据库,要有 Web 接口,计划要和其余的社交网络进行整合,须要提供运行时运营支持的能力,等等。在这里你能够根据你的项目须要选择不一样的功能。经过选择所需的技术,相应的模块将自动添加到咱们预先生成的项目模板的依赖列表中。apache

在咱们继续开发项目以前,让咱们来看一下 Spring Boot 的模块的功能以及它为咱们提供的好处。

Spring Boot 旨在简化应用程序建立入门的过程。 Spring Boot 模块是引导库,其中包含启动特定功能所需的全部相关传递依赖关系的集合。 每一个启动器都有一个特定文件,其中包含全部提供的依赖关系的列表—— spring.provides。 咱们来看一下spring-boot-starter-test定义的连接:spring.provides

spring.provides

咱们看到此文件的内容为:

provides: spring-test, spring-boot, junit, mockito, hamcrest-library

这告诉咱们,经过在咱们的构建中包含 spring-boot-starter-test 做为依赖,咱们将自动得到 spring-test,spring-boot,junit,mockito 和 hamcrest-library。 这些库将为咱们提供全部必要的事情,以便开始为咱们开发的软件编写应用程序测试,而无需手动将这些依赖关系手动添加到构建文件中。

随着40多个模块的提供以及社区的不断增长,咱们极可能发现本身须要与一个至关广泛或流行的框架进行整合,因此咱们可使用其中的模块。

下表列举了比较有名的模块,以便了解每一个模块的使用:

模块 描述
spring-boot-starter Spring Boot 核心模块,提供全部的基础功能。 其余模块都要依赖它,因此没有必要明确声明。
spring-boot-starter-actuator 提供了监视,管理应用程序和审核的功能。
spring-boot-starter-jdbc 提供了链接和使用JDBC数据库,链接池等的支持。
spring-boot-starter-data-jpa 为使用Java Persistence API(如Hibernate等)提供了必要的类库。
spring-boot-starter-data-* 带有“data-*”的集合组件为诸如MongoDB,Data-Rest或Solr之类的数据存储提供支持。
spring-boot-starter-security 为Spring-security提供全部必需的依赖。
spring-boot-starter-social-* 提供了与Facebook, Twitter, 和 LinkedIn 整合的功能。
spring-boot-starter-test 包含Spring-test和各类测试框架(如JUnit和Mockito等)的依赖。
spring-boot-starter-web 提供了Web应用程序开发所需的全部依赖。做为spring-boot-starter-hateoas, spring-boot-starter-websocket, spring-boot-starter-mobile, 和 spring-boot-starter-ws 的补充。以及各类模板渲染模块sping-boot-starter-thymeleaf和spring-boot-starter-mustache。

二. 建立一个简单的应用

如今咱们去http://start.spring.io去建立一个基本的应用。这里须要注意的是,咱们须要展开更多选项。以下图:

Switch to the full version.

咱们要建立的应用程序是一个图书目录管理系统。 它将保存出版的书籍,做者,评论者,出版社等的记录。 咱们将项目命名为 BookPub,具体步骤以下:

  1. 使用一个推荐的 Group 名字:org.test;
  2. 在 Artifact 输入框内输入“bookput”;
  3. 应用的名字为:BookPub;
  4. 包名为:org.test.bookpub;
  5. 选择 Gradle Project;
  6. 打包方式选择 jar;
  7. 使用 Java 的版本为1.8;
  8. 在 Project Dependencies 里面,输入 H2,这时会自动提示,而后选择便可,还要选择 JDBC,JPA。这里咱们使用 H2 数据库。
  9. 最后单击“Generate Project”按钮下载打包文件。
    具体选项以下截图:

具体选项

咱们下载 bookpub.zip 后并解压,会生成 bookpub 目录,在此目录下你会看到build.gradle 文件来定义项目的构建,它已经预先配置了正确版本的 Spring Boot 插件和库,甚至包括咱们选择的额外的模块。

build.gradle 文件里的部份内容以下:

dependencies {
  compile("org.springframework.boot:spring-boot-starter-data-jpa")
  compile("org.springframework.boot:spring-boot-starter-jdbc")
  runtime("com.h2database:h2")
  testCompile("org.springframework.boot:spring-boot-starter-test") 
}

咱们已经选择了以下模块:

  • org.springframework.boot:spring-boot-starter-data-jpa:加入 JPA 的依赖;
  • org.springframework.boot:spring-boot-starter-jdbc:加入 JDBC 支持的类库;
  • com.h2database:h2:特定类型的数据库实现,名字为 H2。

在上面的文件中,你会发现,只有一个运行时依赖:runtime("com.h2database:h2")。这是由于咱们不须要,甚至不想要知道在编译时咱们将链接的数据库的确切类型。 一旦它在启动应用程序时检测到类路径中的org.h2.Driver 类的存在,Spring Boot 将自动配置所需的设置并建立适当的bean。

data-jpa 和 jdbc 是 Spring Boot 模块的artifact。 若是咱们在 Gradle 本地下载,或使用 Maven Central 在线文件存储库的时候查看这些依赖项,咱们会发现它们不包含任何实际的类,只包含各类元数据。 咱们特别感兴趣的两个文件是 Manven 的 pom.xml 和 Gradle 的spring.provides。 咱们先来看一下 spring-boot-starter-jdbc.jar 中的 spring.provides 文件,其中包含如下内容:

provides: spring-jdbc,spring-tx,tomcat-jdbc

这告诉咱们,经过将这个模块作为咱们的依赖关系,咱们将在构建中传递地获取 spring-jdbc,spring-tx 和 tomcat-jdbc 依赖库。 pom.xml 文件包含正确的依赖关系声明,将由 Gradle 或 Maven 用来在构建期间解析所需的依赖关系。 这也适用于咱们的第二个模块:spring-boot-starter-data-jpa。 这个模块将会向咱们提供 spring-orm,hibernate-entity-manager 和 spring-data-jpa 类库。

在这一点上,咱们在应用程序类路径中有足够的库/类,以便给 Spring Boot 一个想要运行的应用程序的想法,以及 Spring Boot 须要自动配置的工具和框架类型把这些模块拼装在一块儿。

早些时候,咱们提到类路径中的 org.h2.Driver 类,在触发 Spring Boot 时为咱们的应用程序自动配置 H2 数据库链接。 要了解其原理,咱们首先来看看咱们新建立的应用程序模板,位于项目根目录下的 src/main/java/org/test/ bookpub 目录中的 BookPubApplication.java,以下所示:

package org.test.bookpub;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BookPubApplication {

    public static void main(String[] args) {
        SpringApplication.run(BookPubApplication.class, args);
    }
}

这其实是咱们整个以及彻底可运行的应用程序。 这里没有不少的代码,也没有说起任何地方的配置或数据库。 但关键是 @SpringBootApplication 注解。 为了了解实际发生的状况,咱们能够看看这个注解代码,这里找到其注解的注解,它们会使 Spring Boot 自动设置一些事情:

@Configuration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {…}

接下来,让咱们看一下上面几个注解的做用:
@Configuration: 告诉 Spring(不仅是Spring Boot,由于它是一个 Spring 框架核心注释),注解类包含 Spring 配置定义声明,例如@Bean@Component@Service 等。
@ComponentScan:告诉 Spring,咱们要扫描咱们的应用程序包 —— 从咱们的注解类的包做为默认的根路径开始扫描 - 可使用 @Configuration@Controller 和其余适合的注解,Spring 将自动引入,做为上下文配置的一部分。
@EnableAutoConfiguration:是 Spring Boot 注解的一部分,它是本身的元注解。 它导入 EnableAutoConfigurationImportSelectorAutoConfigurationPackages.Registrar 类,它们有效地指示 Spring 根据类路径中可用的类自动配置条件bean。

上面代码中的SpringApplication.run(BookPubApplication.class,args);, 在main方法中建立了一个 Spring 应用程序上下文,它读取BookPubApplication.class 中的注解,并实例化,这与前面已经完成的方法相似,而不是使用 Spring Boot,咱们没法摆脱 Spring 框架。

三. 使用 Gradle 启动一个应用

一般状况下,建立任何应用程序的第一步是建立一个基本的骨架,而后能够当即启动。 因为 Spring Boot 模块已经为咱们建立了应用程序模板,因此咱们所要作的就是提取代码,构建和执行它。 如今让咱们去命令行控制台,并用 Gradle 启动应用程序。由于个人操做系统是 macOS,因此我使用 Terminal 控制台来作。

首先,咱们在命令行控制台中进入咱们已经解压好的 bookpub.zip 的目录下,而后执行下面的命令:

./gradlew clean bootRun

下载完的状态是这样的,中间要等上一下子。

build successful

正如咱们所看到的,应用程序启动正常,但因为咱们没有添加任何功能或配置任何服务,它便当即终止了。不管如何,从启动日志总能够看到,自动配置确实发生了。让咱们来看看下面的内容:

Building JPA container EntityManagerFactory for persistence unit 'default'
HHH000412: Hibernate Core {4.3.8.Final}
HHH000400: Using dialect: org.hibernate.dialect.H2Dialect

以上信息说明,由于咱们增长了 jdbc 和 data-jpa 的模块,JPA 容器被建立并使用 h2dialect 方式管理持久层 Hibernate 4.3.8.final版本。这也是由于咱们在 classpath 中配置了正确的类。

四. 使用命令行命令

随着咱们的基本应用骨架准备好了,让咱们添加功能,使咱们的应用程序作一些事情。

首先咱们建立一个类,类名为StartupRunner,它实现 CommandLineRunner 接口,这个接口中只提供了一个方法 public void run(String… args),这个方法将在应用程序启动之后被 Spring Boot 调用一次。

咱们在 bookpub 目录的 src/main/java/org/test/bookpub/ 路径下,建立StartupRunner.java,具体代码为:

package org.test.bookpub;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.CommandLineRunner;

public class StartupRunner implements CommandLineRunner {
    protected final Log logger = LogFactory.getLog(getClass());
  
    @Override
    public void run(String... args) throws Exception {
        logger.info("Hello");
    }
}

接下来在 BookPubApplication.java 文件中,把上面的类标记 @Bean 注解用来注入,具体以下:

package org.test.bookpub;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class BookPubApplication {

    public static void main(String[] args) {
        SpringApplication.run(BookPubApplication.class, args);
    }

    @Bean
    public StartupRunner schedulerRunner() {
        return new StartupRunner();
    }
}

接着,在命令行中执行./gradlew clean bootRun,

output Hello

在启动过程当中的日志里输出了 “Hello” 字符串。

即便程序将被终止执行,至少咱们让它作一些事!

命令行的运行是一个有用的功能,用来执行各类类型的代码,只须要运行一次后,应用程序启动。有些人也可使用这个做为一种启动各类执行器线程的方式,但 Spring 启动提供了一个更好方式解决这个任务。CommandLineRunner接口由 Spring Boot 启动之后扫描改接口全部的实现,调用的每一个实例的带有启动参数的 run 方法。咱们也可使用 @Order 注解或实现 Ordered 接口,以便定义咱们想要 Spring Boot 来执行它们的确切顺序。例如,Spring 批处理依赖 runner 类以便触发 job 的执行。

当命令行运行器在应用程序启动后实例化并执行时,咱们可使用依赖注的优点来绑定咱们所须要的依赖(例如数据源、服务和其余组件)。当在实现run(String... args)方法后来使用。

Tips
须要注意的是,若是在run(String… args)方法内有异常抛出,这将致使上下文和应用程序的关闭。为了不这种状况发生,建议用 try/catch 包装有风险的代码块。

五. 设置数据库链接

在每一个应用程序中,须要访问一些数据并对其进行一些操做。最多见的,这个数据源是某种数据存储,即数据库。Spring Boot 采起了很是简单容易的方式,以便链接到数据库,并使用 JPA 来访问和操做数据。

在前面的示例中,咱们建立了基本应用程序,在命令行中启动应用并在日志中打印一条消息。接下来,咱们加强这个应用,给他添加数据库链接的功能。

此前,咱们已经添加必要的 jdbc 和 data-jpa 模块,以及 H2 数据库的依赖构建文件。如今,咱们将配置 H2 数据库的内存实例。

Tips
当使用嵌入式数据库时,如H2,HSQL,或者 Derby,没有真正必需的配置,此外包括在构建文件中的依赖关系。当这些数据库在类路径中检测到DataSource这个 bean 的依赖在代码里声明时,Spring Boot 会自动给你建立一个。

为了演示这一状况,如今只包括在类路径中的 H2 的依赖,咱们将自动得到一个默认数据库,下面修改咱们以前的StartupRunner.java文件:

package org.test.bookpub;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.CommandLineRunner;

public class StartupRunner implements CommandLineRunner {
    protected final Log logger = LogFactory.getLog(getClass());

    @Autowired
    private DataSource ds;

    @Override
    public void run(String... args) throws Exception {
        logger.info("DataSource: " + ds.toString());
    }
}

如今,咱们继续应用程序的运行,咱们在日志里看到数据源的名称,以下:

数据源名称

因此,在框架引擎下,Spring 会意识到自动装配数据源的依赖并自动建立一个初始化内存 H2 数据库。这一切看起来还不错,但只是在早期原型阶段或测试目的,其余场景并非颇有用。一旦应用程序关闭,内存数据库的数据将会所有消失,不会保留。

那如何才能持久保留数据呢?能够更改默认值,以建立一个嵌入式 H2 数据库,它不会将数据存储在内存中,而是使用一个文件来在应用程序重启之间保持数据。

在src/main/resources目录下打开application.properties文件,添加如下内容:

spring.datasource.url = jdbc:h2:~/test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username = sa
spring.datasource.password =

接下来执行./gradlew clean bootRun。执行之后,就会在你的系统主目录下生成test.mv.db文件。

Tips
如何你使用的是 Linux 系统,test.mv.db会生成在/home/<username>下。
若是是 macOS,则在/Users/<username>下。

默认状况下,Spring Boot 经过检查类路径支持的数据库驱动程序的存在使得对数据库配置进行必定的假设,经过配置文件中的spring.datasource.*属性组,从而提供了很是容易的配置选项来调整数据库的访问。

咱们能够配置 url, username, password, driver-class-name 等选项。若是你想使用JNDI方式访问数据源,建立应用程序以外的数据源实例,例如经过一个容器,如 JBoss、Tomcat、和经过 JNDI 共享,能够配置 spring.datasource.jndiname。

Tips
在配置文件的属性名字中,例如,driver-class-name,和 driverClassName,二者都是支持的,Spring Boot 会把它们转换成同一种方式。

若是你想链接到一个常规(非嵌入式)数据库,除了在类路径中添加适当的驱动程序库,咱们须要指定的配置中选择驱动程序。下面的片断是 MySQL 的配置信息:

spring.datasource.driver-class-name: com.mysql.jdbc.Driver
spring.datasource.url: jdbc:mysql://localhost:3306/springbootcookbook
spring.datasource.username: root
spring.datasource.password:

若是咱们但愿 Hibernate 基于咱们的实体类,自动建立 schema,须要添加下面的配置属性:

spring.jpa.hibernate.ddl-auto=create-drop

Tips
在上面的配置属性中,不要在生产环境中使用,不然在启动时,全部的表模式和数据都会被删除!而是根据须要,使用 update 或 validate 属性值。

你能够在应用程序的抽象层再进一步,再也不自动装配 DataSource 对象,而是直接用 jdbcTemplate。这将指示 Spring Boot 自动建立一个数据源,而后建立一个JdbcTemplate 对象包装数据源,从而为您提供更方便的方式与数据库安全的交互。JdbcTemplate的代码以下:

@Autowired
private JdbcTemplate jdbcTemplate;

若是你对此处保持好奇的心态,能够查看 spring-boot-autoconfigure 模块下的 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration类,就会豁然开朗。

六. 创建一个数据库

链接到数据库,而后执行良好的SQL语句,这是简单和直接的方式,而不是最方便的方式操做数据,而更好的方式是映射在一组领域对象,并操纵关系的内容。这就是为何出现了不少框架实现了将数据从表到对象的映射,也就是你们常说的 ORM(Object Relational Mapping)。其中一个最有名的框架就是 Hibernate。

在前面的例子中,咱们介绍了如何创建一个链接到数据库和配置设置的用户名,密码,使用哪一个驱动程序,等等。咱们将加强应用程序,根据数据库中数据结构的定义,添加对应的实体对象, 使用 crudrepository 接口访问数据。

根据咱们应用程序的应用场景,是一个图书查找分类的系统,因此会包括Book,,Author,,Reviewers,和 Publisher 这些实体对象。

接下来,在 src/main/java/org/test/bookpub 目录下,建立 entity 包;

在 entity 包下,建立 Book.java 文件,代码以下:

package org.test.bookpub.entity;

import javax.persistence.*;
import java.util.List;

@Entity
public class Book {
    @Id
    @GeneratedValue
    private Long id;
    private String isbn;
    private String title;
    private String description;

    @ManyToOne
    private Author author;

    @ManyToOne
    private Publisher publisher;

    @ManyToMany
    private List<Reviewer> reviewers;

    protected Book() {}

    public Book(String isbn, String title, Author author, Publisher publisher) {
        this.isbn = isbn;
        this.title = title;
        this.author = author;
        this.publisher = publisher;
    }

   // 省略属性的 getter 和 setter 方法
}

任何一本书都会有一个做者和出版社,还会有不少评论者,因此, 咱们也要建立这些对应的实体对象,在 Book.java 同目录下,建立 Author.java

@Entity
public class Author {
  @Id
  @GeneratedValue
  private Long id;
  private String firstName;
  private String lastName;
  @OneToMany(mappedBy = "author")
  private List<Book> books;

  protected Author() {}

  public Author(String firstName, String lastName) {...}
    // 省略购房方法属性赋值
}

// 省略 属性 getter 和 setter 方法

一样,建立 Publisher.javaReviewer.java 文件。

@Entity
public class Publisher {
  @Id
  @GeneratedValue
  private Long id;
  private String name;
  @OneToMany(mappedBy = "publisher")
  private List<Book> books;

  protected Publisher() {}

  public Publisher(String name) {...}
}
@Entity
  public class Reviewer {
    @Id
    @GeneratedValue
    private Long id;
    private String firstName;
    private String lastName;

    protected Reviewer() {}

    public Reviewer(String firstName, String lastName) {
      ...
    }
}

下一步,咱们在 src/main/java/org/test/bookpub/repository 目录下建立 BookRepository.java ,并继承 Spring 的CrudRepository 父类,

package org.test.bookpub.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import org.test.bookpub.entity.Book;

@Repository
public interface BookRepository extends CrudRepository<Book, Long> {
    public Book findBookByIsbn(String isbn);
}

最后,修改 StartupRunner.java 文件,用来打印图书的数量,经过自动装配 BookRepository 接口的实例,并调用 .count() 方法。

package org.test.bookpub;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.scheduling.annotation.Scheduled;
import org.test.bookpub.repository.BookRepository;

public class StartupRunner implements CommandLineRunner {
    protected final Log logger = LogFactory.getLog(getClass());

    @Autowired private BookRepository bookRepository;

    @Override
    public void run(String... args) throws Exception {
        logger.info("Welcome to the Book Catalog System!");
    }

    public void run() {
        logger.info("Number of books: " + bookRepository.count());
    }
}

您可能已经注意到,咱们没有写一行SQL,甚至没有提到任何关于数据库链接,构建查询或相似的事情。 咱们处理数据库支持的数据的惟一提示是咱们的代码中的类和属性注解:@Entity,@Repository,@Id,@GeneratedValue 和 @ManyToOne以及 @ ManyToMany 和 @OneToMany。 这些注解是 Java Persistance API的一部分,以及 CrudRepository 接口的扩展,咱们与 Spring 通讯的方式是将咱们的对象映射到数据库中相应的表和字段,并向咱们提供编程与这些数据交互的能力。

咱们来看一下具体注解的使用:

  • @Entity:表示实体对象映射到数据库表的注解类。 表的名称将从类的名称派生,但若是须要,能够进行配置。 重要的是要注意,每一个实体类都应该有一个默认的保护的构造函数,这是自动实例化和Hibernate交互所须要的。
  • @Repository:表示该接口旨在提供对数据库的数据的访问和操做。 它也能够做为组件扫描期间 Spring 的一个指示,即该实例能够做为一个 bean 建立,并将其注入应用程序中的其余 bean 中。
  • CrudRepository接口:定义了从数据存储库读取,建立,更新和删除数据的基本经常使用方法。 咱们将在 BookRepository 扩展中定义的额外方法 public Book findBookByIsbn(String isbn),表示 Spring JPA 应该自动将对该方法的调用转换为经过其 ISBN 字段选择 Book 的 SQL 查询。 这是一个约定命名的映射,将方法名称转换为 SQL 查询。 这是一个很是强大的功能,容许构建查询,如 `findByNameIgnoringCase(String name)等其余的方法。
  • @Id 和 @GeneratedValue:这两个注解提供了一个标记,即注解属性应映射到数据库中的主键字段上,而且应生成此字段的值,而不须要显式地输入。
  • @ManyToOne 和 @ManyToMany:这两个注解定义了引用存储在其余表中的数据的字段关联关系。 在咱们的应用中,多本图书属于一个做者,许多评论者都会评论多本图书。 @OneToMany 注解声明中的 mappedBy 属性定义了反向关联映射。 它表示 Hibernate 的真实的映射源在 Book 类中,在 Author 或 Reviewer 字段中定义。Author 和 Reviewer 类中的 Book 引用仅仅是反向关联。

Tips
有关Spring Data的全部功能的更多信息,请访问http://docs.spring.io/spring-data/data-commons/docs/current/reference/html/

七. 计划执行

在本章以前,咱们讨论了如何使用命令行运行程序做为启动计划的执行程序线程池的方式,用来间隔运行工做线程。 虽然这是一个可能性,但 Spring 提供了更简洁的配置来实现相同的目的:@EnableScheduling注解。

咱们将增强咱们的应用程序,以便它每10秒在咱们的存储库中打印一些图书数量。 为了实现这一点,咱们将对 BookPubApplicationStartupRunner 类进行必要的修改。

首先,咱们须要在 BookPubApplication 类上添加 @EnableScheduling 注解,

@SpringBootApplication
@EnableScheduling
public class BookPubApplication {…}

因为 @Scheduled 注解只能放置在没有参数的方法上,因此咱们将一个新的 run() 方法添加到 StartupRunne r类中,并使用 @Scheduled 注解,以下所示:

package org.test.bookpub;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.scheduling.annotation.Scheduled;
import org.test.bookpub.repository.BookRepository;

public class StartupRunner implements CommandLineRunner {
    protected final Log logger = LogFactory.getLog(getClass());

    @Autowired private BookRepository bookRepository;

    @Override
    public void run(String... args) throws Exception {
        logger.info("Welcome to the Book Catalog System!");
    }

    @Scheduled(initialDelay = 1000, fixedRate = 10000)
    public void run() {
        logger.info("Number of books: " + bookRepository.count());
    }
}

接下来,在命令行中执行./gradlew clean bootRun,在日志中就会每间隔10秒打印出 “Number of books: 0” 的消息。

计划执行

像咱们在本章中讨论的一些其余注解同样,@EnableScheduling 不是 Spring Boot里的注解,而是一个 Spring Context 模块里的注解。 相似于 @SpringBootApplication 和 @EnableAutoConfiguration 注解,它们都是元注释,并经过 @Import(SchedulingConfiguration.class)指令在内部导入 SchedulingConfiguration,若是在 @EnableScheduling 注解类的代码中查找,能够看到它。

将由导入的配置建立的 ScheduledAnnotationBeanPostProcessor 类扫描已声明的 Spring Bean 的 @Scheduled 注解。 对于每一个没有参数的注释方法,将会建立适当的执行程序线程池。 它将管理已添加注解方法的计划调用。