文 by / 林本託html
Tip
作一個終身學習的人。java
代碼地址:
https://github.com/iqcz/Springbootdemomysql
Spring Boot 包含了不少 start(Spring boot 中 的叫法,就是一個模塊,後文統一稱模塊,便於理解),這些模塊其實早就是 Spring Boot 你們庭的成員。這章主要介紹http://start.spring.io/,Spring Boot 提供的可用的組建,經過這個連接咱們能夠快速搭建一個項目。git
這章主要包括如下內容:github
在當今軟件開發快節奏的世界中,應用程序建立的速度和快速原型的需求正在變得愈來愈重要。 若是您正在使用 JVM 語言開發軟件,那麼 Spring Boot 就是一種可以爲您提供靈活性的框架,從而使您可以以快速的速度生產高質量的軟件。 因此,讓咱們來看看 Spring Boot 如何幫助你實現你的應用程序。web
Spring Boot 提供了超過40種不一樣的模塊,它們爲許多不一樣的框架提供即用型集成庫,例如關係型和 NoSQL 的數據庫鏈接,Web 服務,社交網絡集成,監控庫,日誌記錄,模板渲染, 並且這個名單一直在擴大。 雖然覆蓋這些組件中的每個功能不是實際可行的,可是咱們將會重點介紹一些重要和受歡迎的組件,以便了解 Spring Boot 爲咱們提供的可能性和易用性。spring
咱們將從建立一個基本的簡單項目框架開始,Spring Boot 將幫助咱們:sql
網站的截圖以下:數據庫
在上面截圖中,你會看到「Project Dependencies」部分,若是你的項目須要鏈接數據庫,要有 Web 接口,計劃要和其餘的社交網絡進行整合,須要提供運行時運營支持的能力,等等。在這裏你能夠根據你的項目須要選擇不一樣的功能。經過選擇所需的技術,相應的模塊將自動添加到咱們預先生成的項目模板的依賴列表中。apache
在咱們繼續開發項目以前,讓咱們來看一下 Spring Boot 的模塊的功能以及它爲咱們提供的好處。
Spring Boot 旨在簡化應用程序建立入門的過程。 Spring Boot 模塊是引導庫,其中包含啓動特定功能所需的全部相關傳遞依賴關係的集合。 每一個啓動器都有一個特定文件,其中包含全部提供的依賴關係的列表—— spring.provides。 咱們來看一下spring-boot-starter-test定義的連接: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去建立一個基本的應用。這裏須要注意的是,咱們須要展開更多選項。以下圖:
咱們要建立的應用程序是一個圖書目錄管理系統。 它將保存出版的書籍,做者,評論者,出版社等的記錄。 咱們將項目命名爲 BookPub,具體步驟以下:
咱們下載 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") }
咱們已經選擇了以下模塊:
在上面的文件中,你會發現,只有一個運行時依賴: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 註解的一部分,它是本身的元註解。 它導入 EnableAutoConfigurationImportSelector
和 AutoConfigurationPackages.Registrar
類,它們有效地指示 Spring 根據類路徑中可用的類自動配置條件bean。
上面代碼中的SpringApplication.run(BookPubApplication.class,args);
, 在main
方法中建立了一個 Spring 應用程序上下文,它讀取BookPubApplication.class 中的註解,並實例化,這與前面已經完成的方法相似,而不是使用 Spring Boot,咱們沒法擺脫 Spring 框架。
一般狀況下,建立任何應用程序的第一步是建立一個基本的骨架,而後能夠當即啓動。 因爲 Spring Boot 模塊已經爲咱們建立了應用程序模板,因此咱們所要作的就是提取代碼,構建和執行它。 如今讓咱們去命令行控制檯,並用 Gradle 啓動應用程序。由於個人操做系統是 macOS,因此我使用 Terminal 控制檯來作。
首先,咱們在命令行控制檯中進入咱們已經解壓好的 bookpub.zip 的目錄下,而後執行下面的命令:
./gradlew clean bootRun
下載完的狀態是這樣的,中間要等上一下子。
正如咱們所看到的,應用程序啓動正常,但因爲咱們沒有添加任何功能或配置任何服務,它便當即終止了。不管如何,從啓動日誌總能夠看到,自動配置確實發生了。讓咱們來看看下面的內容:
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
,
在啓動過程當中的日誌裏輸出了 「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.java
和 Reviewer.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 通訊的方式是將咱們的對象映射到數據庫中相應的表和字段,並向咱們提供編程與這些數據交互的能力。
咱們來看一下具體註解的使用:
public Book findBookByIsbn(String isbn)
,表示 Spring JPA 應該自動將對該方法的調用轉換爲經過其 ISBN 字段選擇 Book 的 SQL 查詢。 這是一個約定命名的映射,將方法名稱轉換爲 SQL 查詢。 這是一個很是強大的功能,容許構建查詢,如 `findByNameIgnoringCase(String name)等其餘的方法。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秒在咱們的存儲庫中打印一些圖書數量。 爲了實現這一點,咱們將對 BookPubApplication
和 StartupRunner
類進行必要的修改。
首先,咱們須要在 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 註解。 對於每一個沒有參數的註釋方法,將會建立適當的執行程序線程池。 它將管理已添加註解方法的計劃調用。