個人積累康二,請勿私自轉(zhuǎn)載,轉(zhuǎn)載前請聯(lián)系
代碼及文章資源https://github.com/jedyang/DayDayUp/tree/master/java/springboot
基于SpringBootCookBook的讀書筆記腰鬼,重在個人理解和實踐,而非翻譯。
入門
在現(xiàn)代快節(jié)奏的軟件開發(fā)中缺狠,快速開發(fā)和原型設(shè)計變得越來越重要器净。如果你是使用JVM平臺語言(不只是java哦)型雳,那springboot是加快開發(fā)速度的利器。 下面將如何將工程boot化山害。
使用springboot的template和starter
springboot包含了40多個不同的starter模塊纠俭,為各種功能提供了即時可用的library。如數(shù)據(jù)庫連接浪慌,監(jiān)控冤荆,web服務(wù)等等。 不會一個一個介紹权纤,看幾個最重要的钓简。
去https://start.spring.io/生成一個最簡單的springboot應(yīng)用乌妒。
在這個頁面上可以選擇需要的dependency。在生成的工程中會有對應(yīng)的starter外邓。比如撤蚊,我選了mysql
,mybatis
。生成的pom中有:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
那么损话,springboot的starter究竟是什么侦啸?
springboot的目標(biāo)是簡化系統(tǒng)的構(gòu)建。starter就是啟動一個特定工程需要的一系列依賴的集合席镀。
每個starter都有一個特殊的文件spring.provides匹中,如starter-test,https://github.com/spring-projects/spring-boot/blob/master/spring-boot-starters/spring-boot-starter-test/src/main/resources/META-INF/spring.provides
可以看到它集成了provides: spring-test,spring-boot,junit,mockito,hamcrest-library,assertj,jsonassert,json-path豪诲。
這樣我們不在需要手動添加這些依賴顶捷。
常用的starter如下:
- spring-boot-starter: 這是核心Spring Boot starter,提供了大部分基礎(chǔ)功能屎篱,其他starter都依賴于它服赎,因此沒有必要顯式定義它。
- spring-boot-starter-actuator:主要提供監(jiān)控交播、管理和審查應(yīng)用程序的功能重虑。
- spring-boot-starter-jdbc:該starter提供對JDBC操作的支持,包括連接數(shù)據(jù)庫秦士、操作數(shù)據(jù)庫缺厉,以及管理數(shù)據(jù)庫連接等等。
- spring-boot-starter-data-jpa:JPA starter提供使用Java Persistence API(例如Hibernate等)的依賴庫隧土。
- spring-boot-starter-data-*:提供對MongoDB提针、Data-Rest或者Solr的支持。
- spring-boot-starter-security:提供所有Spring-security的依賴庫曹傀。
- spring-boot-starter-test:這個starter包括了spring-test依賴以及其他測試框架辐脖,例如JUnit和Mockito等等。
- spring-boot-starter-web:該starter包括web應(yīng)用程序的依賴庫皆愉。
動手建一個demo工程
一個簡單的圖書管理系統(tǒng)嗜价。在spring的創(chuàng)建頁面上。
- Group設(shè)置為:org.test幕庐;
- Artifact設(shè)置為:bookpub久锥;
- Name設(shè)置為:BookPub;
- Package Name設(shè)置為:org.test.bookpub翔脱;
- Packaging代表打包方式奴拦,我們選jar;
- Spring Boot Version選擇最新的1.3.0届吁;
- 創(chuàng)建Maven工程错妖,當(dāng)然绿鸣,對Gradle比較熟悉的同學(xué)可以選擇Gradle工程。
- 依賴添加H2暂氯,JDBC,JPA
- 點擊“Generate Project”下載工程包潮模。
打開pom文件,可以看到對應(yīng)的starter痴施。
然后看一下主代碼擎厢。
@SpringBootApplication
public class BookPubApplication {
public static void main(String[] args) {
SpringApplication.run(BookPubApplication.class, args);
}
}
就是這么少,沒有配置文件辣吃,也沒有數(shù)據(jù)庫配置动遭。但是可以運行,實現(xiàn)這個魔法的是注解@SpringBootApplication
神得。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {...}
注意厘惦,@SpringBootConfiguration是被@Configuration注解,@Configuration表示被注解的類包含spring配置定義哩簿。
@ComponentScan宵蕉,包掃描。默認(rèn)以該類所在的包為根路徑掃描节榜。
@EnableAutoConfiguration羡玛,這個是spring-boot新引入的注解作用是自動配置。
在main方法中SpringApplication.run(BookPubApplication.class, args);
讀取BookPubApplication的注解宗苍,并初始化context稼稿。
啟動看下,使用mvn spring-boot:run
在對應(yīng)目錄啟動讳窟,觀察啟動成功渺杉。
順便安利下H2數(shù)據(jù)庫,是一個內(nèi)存數(shù)據(jù)庫挪钓,也支持持久化,個人感覺很好用耳舅。請搜索相關(guān)文檔碌上。
使用Command-line runners
讓我們加點代碼.
新增一個類。
package org.test.bookpub;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.CommandLineRunner;
/**
* Created by yangyunsheng on 2017/7/12.
*/
public class StartupRunner implements CommandLineRunner{
Log logger = LogFactory.getLog(getClass());
@Override
public void run(String... strings) throws Exception {
logger.info("hello");
}
}
在BookPubApplication中增加bean注入:
@Bean
public StartupRunner schedulerRunner() {
return new StartupRunner();
}
可以看到成功的輸出:
2017-07-12 19:10:08.141 INFO 11032 --- [ main] org.test.bookpub.StartupRunner : hello
CommandLineRunner適用于那種在程序啟動時只執(zhí)行一次的任務(wù)浦徊,如各種資源的加載馏予。
springboot會掃描實現(xiàn)CommandLineRunner的類,執(zhí)行其中的run方法盔性。
run方法的參數(shù)是啟動函數(shù)(如main函數(shù))傳入的霞丧。
另外,可以使用@Order注解或者實現(xiàn)Order接口冕香,來控制多個Runner的執(zhí)行順序蛹尝。通過設(shè)置value的值后豫,值越小,執(zhí)行越早突那。
注意:因為CommandLineRunner是執(zhí)行在啟動階段挫酿,一旦報錯將阻斷整個應(yīng)用,所以一定記得用try-catch處理異常愕难。
建立數(shù)據(jù)庫連接
修改一下代碼:
@Order(value = 1)
public class StartupRunner implements CommandLineRunner{
protected final Log logger = LogFactory.getLog(getClass());
@Autowired
private DataSource ds;
@Override
public void run(String... strings) throws Exception {
logger.info("dataSource:" + ds.toString());
}
}
執(zhí)行早龟,看日志:
2017-07-13 09:32:49.464 INFO 8132 --- [ main] org.test.bookpub.StartupRunner : dataSource:org.apache.tomcat.jdbc.pool.DataSource@f107c50{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=org.h2.Driver; .... }
可以看到,因為H2是一種內(nèi)存數(shù)據(jù)庫猫缭,我們僅需要引入H2的依賴葱弟,執(zhí)行時如果沒有H2實例,程序會自動創(chuàng)建一個H2數(shù)據(jù)庫猜丹。 但是這樣每次結(jié)束應(yīng)用芝加,數(shù)據(jù)都會丟失。幸運的是居触,H2支持?jǐn)?shù)據(jù)持久化妖混,我們可以將數(shù)據(jù)保存到文件里。繼續(xù)修改代碼:
在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 =
再次執(zhí)行轮洋,會在你的用戶目錄下生產(chǎn)test.mv.db數(shù)據(jù)庫文件制市。
連接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自動創(chuàng)建schemal,再加一個:
spring.jpa.hibernate.ddl-auto=create-drop
這里多說一句: ddl-auto的參數(shù)有:validate | update | create | create-drop
其實這個ddl-auto參數(shù)的作用主要用于:自動創(chuàng)建|更新|驗證數(shù)據(jù)庫表結(jié)構(gòu)弊予。如果不是此方面的需求建議set value="none"祥楣。
- create:每次加載hibernate時都會刪除上一次的生成的表,然后根據(jù)你的model類再重新來生成新表汉柒,哪怕兩次沒有任何改變也要這樣執(zhí)行误褪,這就是導(dǎo)致數(shù)據(jù)庫表數(shù)據(jù)丟失的一個重要原因。
- create-drop :每次加載hibernate時根據(jù)model類生成表碾褂,但是sessionFactory一關(guān)閉,表就自動刪除兽间。生產(chǎn)環(huán)境不要用。
- update:最常用的屬性正塌,第一次加載hibernate時根據(jù)model類會自動建立起表的結(jié)構(gòu)(前提是先建立好數(shù)據(jù)庫)嘀略,以后加載hibernate時根據(jù) model類自動更新表結(jié)構(gòu),即使表結(jié)構(gòu)改變了但表中的行仍然存在不會刪除以前的行乓诽。要注意的是當(dāng)部署到服務(wù)器后帜羊,表結(jié)構(gòu)是不會被馬上建立起來的,是要等 應(yīng)用第一次運行起來后才會鸠天。
- validate :每次加載hibernate時讼育,驗證創(chuàng)建數(shù)據(jù)庫表結(jié)構(gòu),只會和數(shù)據(jù)庫中的表進(jìn)行比較,不會創(chuàng)建新表奶段,但是會插入新值饥瓷。
再說點“廢話”:
當(dāng)我們把hibernate.hbm2ddl.auto=create時hibernate先用hbm2ddl來生成數(shù)據(jù)庫schema。
當(dāng)我們把hibernate.cfg.xml文件中hbm2ddl屬性注釋掉忧饭,這樣我們就取消了在啟動時用hbm2ddl來生成數(shù)據(jù)庫schema扛伍。通常 只有在不斷重復(fù)進(jìn)行單元測試的時候才需要打開它,但再次運行hbm2ddl會把你保存的一切都刪除掉(drop)---- create配置的含義是:“在創(chuàng)建SessionFactory的時候词裤,從scema中drop掉所以的表刺洒,再重新創(chuàng)建它們”。
注意吼砂,很多Hibernate新手在這一步會失敗逆航,我們不時看到關(guān)于Table not found錯誤信息的提問。但是渔肩,只要你根據(jù)上面描述的步驟來執(zhí)行因俐,就不會有這個問題,因為hbm2ddl會在第一次運行的時候創(chuàng)建數(shù)據(jù)庫schema周偎, 后續(xù)的應(yīng)用程序重啟后還能繼續(xù)使用這個schema抹剩。假若你修改了映射,或者修改了數(shù)據(jù)庫schema,你必須把hbm2ddl重新打開一次蓉坎。
數(shù)據(jù)操作服務(wù)
當(dāng)前操作數(shù)據(jù)一般都是使用ORM框架澳眷,這里以hibernate為例。
代碼較多蛉艾,先建立幾個實體對象(省略getset):
@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<Publisher.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;
}
@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) {
this.firstName = firstName;
this.lastName = lastName;
}
@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) {
this.name = name;
}
@Entity
public class Reviewer {
@Id
@GeneratedValue
private Long id;
private String firstName;
private String lastName;
protected Reviewer() {
}
public Reviewer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
然后創(chuàng)建一個BookRepository接口:
@Repository
public interface BookRepository extends CrudRepository<Book, Long> {
public Book findByIsbn(String isbn);
}
修改一下starter:
@Autowired
private BookRepository bookRepository;
@Override
public void run(String... strings) throws Exception {
logger.info("number of books:" + bookRepository.count());
}
可以看到钳踊,我們并沒有寫任何sql。只是用了很多注解勿侯,做了對象和表結(jié)構(gòu)的映射拓瞪。然后繼承CrudRepository。
- @Entity:注解類助琐,映射成表祭埂。注意類要有一個protect的默認(rèn)構(gòu)造器。
- @Repository 注解的接口兵钮,表示提供對數(shù)據(jù)庫的操作服務(wù)沟堡。同時spring會為這個接口創(chuàng)建對應(yīng)的bean,以備注入使用矢空。
- CrudRepository提供了基本的CRUD操作。其他的操做禀横,如findByIsbn屁药。需要根據(jù)命名習(xí)慣,spring會自動解析柏锄,如s findByNameIgnoringCase酿箭。
- @Id @GeneratedValue 自增主鍵
- @ManyToOne @ManyToMany @OneToMany表示兩個實體的對應(yīng)關(guān)系复亏。如book和author是多對一的關(guān)系。
調(diào)度執(zhí)行器
實現(xiàn)每10秒執(zhí)行一次缭嫡。
在BookPubApplication加上@EnableScheduling注解缔御。
在starter加一個方法
@Scheduled(initialDelay = 1000, fixedRate = 10000)
public void run(){
logger.info("number of books:" + bookRepository.count());
}
原理可以看下,@EnableScheduling注解妇蛀,它引入SchedulingConfiguration類耕突。這個類會創(chuàng)建一個ScheduledAnnotationBeanPostProcessor。它會掃描被@Scheduled注解的無參方法评架。注意方法一定要是無參的眷茁。