springboot(1)

個(gè)人積累,請(qǐng)勿私自轉(zhuǎn)載,轉(zhuǎn)載前請(qǐng)聯(lián)系
代碼及文章資源https://github.com/jedyang/DayDayUp/tree/master/java/springboot
基于SpringBootCookBook的讀書筆記,重在個(gè)人理解和實(shí)踐,而非翻譯。

入門

在現(xiàn)代快節(jié)奏的軟件開發(fā)中,快速開發(fā)和原型設(shè)計(jì)變得越來越重要。如果你是使用JVM平臺(tái)語言(不只是java哦),那springboot是加快開發(fā)速度的利器。 下面將如何將工程boot化。

使用springboot的template和starter

springboot包含了40多個(gè)不同的starter模塊,為各種功能提供了即時(shí)可用的library。如數(shù)據(jù)庫連接,監(jiān)控,web服務(wù)等等。 不會(huì)一個(gè)一個(gè)介紹,看幾個(gè)最重要的。

https://start.spring.io/生成一個(gè)最簡(jiǎn)單的springboot應(yīng)用。
在這個(gè)頁面上可以選擇需要的dependency。在生成的工程中會(huì)有對(duì)應(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)是簡(jiǎn)化系統(tǒng)的構(gòu)建。starter就是啟動(dòng)一個(gè)特定工程需要的一系列依賴的集合。
每個(gè)starter都有一個(gè)特殊的文件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。
這樣我們不在需要手動(dòng)添加這些依賴。

常用的starter如下:

  • spring-boot-starter: 這是核心Spring Boot starter,提供了大部分基礎(chǔ)功能,其他starter都依賴于它,因此沒有必要顯式定義它。
  • spring-boot-starter-actuator:主要提供監(jiān)控、管理和審查應(yīng)用程序的功能。
  • spring-boot-starter-jdbc:該starter提供對(duì)JDBC操作的支持,包括連接數(shù)據(jù)庫、操作數(shù)據(jù)庫,以及管理數(shù)據(jù)庫連接等等。
  • spring-boot-starter-data-jpa:JPA starter提供使用Java Persistence API(例如Hibernate等)的依賴庫。
  • spring-boot-starter-data-*:提供對(duì)MongoDB、Data-Rest或者Solr的支持。
  • spring-boot-starter-security:提供所有Spring-security的依賴庫。
  • spring-boot-starter-test:這個(gè)starter包括了spring-test依賴以及其他測(cè)試框架,例如JUnit和Mockito等等。
  • spring-boot-starter-web:該starter包括web應(yīng)用程序的依賴庫。

動(dòng)手建一個(gè)demo工程

一個(gè)簡(jiǎn)單的圖書管理系統(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)然,對(duì)Gradle比較熟悉的同學(xué)可以選擇Gradle工程。
  • 依賴添加H2,JDBC,JPA
  • 點(diǎn)擊“Generate Project”下載工程包。

打開pom文件,可以看到對(duì)應(yīng)的starter。
然后看一下主代碼。


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

就是這么少,沒有配置文件,也沒有數(shù)據(jù)庫配置。但是可以運(yùn)行,實(shí)現(xiàn)這個(gè)魔法的是注解@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,這個(gè)是spring-boot新引入的注解作用是自動(dòng)配置。

在main方法中SpringApplication.run(BookPubApplication.class, args);讀取BookPubApplication的注解,并初始化context。

啟動(dòng)看下,使用mvn spring-boot:run在對(duì)應(yīng)目錄啟動(dòng),觀察啟動(dòng)成功。

順便安利下H2數(shù)據(jù)庫,是一個(gè)內(nèi)存數(shù)據(jù)庫,也支持持久化,個(gè)人感覺很好用。請(qǐng)搜索相關(guān)文檔。

使用Command-line runners

讓我們加點(diǎn)代碼.
新增一個(gè)類。

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適用于那種在程序啟動(dòng)時(shí)只執(zhí)行一次的任務(wù),如各種資源的加載。

springboot會(huì)掃描實(shí)現(xiàn)CommandLineRunner的類,執(zhí)行其中的run方法。
run方法的參數(shù)是啟動(dòng)函數(shù)(如main函數(shù))傳入的。

另外,可以使用@Order注解或者實(shí)現(xiàn)Order接口,來控制多個(gè)Runner的執(zhí)行順序。通過設(shè)置value的值,值越小,執(zhí)行越早。

注意:因?yàn)镃ommandLineRunner是執(zhí)行在啟動(dòng)階段,一旦報(bào)錯(cuò)將阻斷整個(gè)應(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; .... }

可以看到,因?yàn)镠2是一種內(nèi)存數(shù)據(jù)庫,我們僅需要引入H2的依賴,執(zhí)行時(shí)如果沒有H2實(shí)例,程序會(huì)自動(dòng)創(chuàng)建一個(gè)H2數(shù)據(jù)庫。 但是這樣每次結(jié)束應(yīng)用,數(shù)據(jù)都會(huì)丟失。幸運(yùn)的是,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í)行,會(huì)在你的用戶目錄下生產(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自動(dòng)創(chuàng)建schemal,再加一個(gè):

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

這里多說一句: ddl-auto的參數(shù)有:validate | update | create | create-drop

其實(shí)這個(gè)ddl-auto參數(shù)的作用主要用于:自動(dòng)創(chuàng)建|更新|驗(yàn)證數(shù)據(jù)庫表結(jié)構(gòu)。如果不是此方面的需求建議set value="none"。

  • create:每次加載hibernate時(shí)都會(huì)刪除上一次的生成的表,然后根據(jù)你的model類再重新來生成新表,哪怕兩次沒有任何改變也要這樣執(zhí)行,這就是導(dǎo)致數(shù)據(jù)庫表數(shù)據(jù)丟失的一個(gè)重要原因。
  • create-drop :每次加載hibernate時(shí)根據(jù)model類生成表,但是sessionFactory一關(guān)閉,表就自動(dòng)刪除。生產(chǎn)環(huán)境不要用。
  • update:最常用的屬性,第一次加載hibernate時(shí)根據(jù)model類會(huì)自動(dòng)建立起表的結(jié)構(gòu)(前提是先建立好數(shù)據(jù)庫),以后加載hibernate時(shí)根據(jù) model類自動(dòng)更新表結(jié)構(gòu),即使表結(jié)構(gòu)改變了但表中的行仍然存在不會(huì)刪除以前的行。要注意的是當(dāng)部署到服務(wù)器后,表結(jié)構(gòu)是不會(huì)被馬上建立起來的,是要等 應(yīng)用第一次運(yùn)行起來后才會(huì)。
  • validate :每次加載hibernate時(shí),驗(yàn)證創(chuàng)建數(shù)據(jù)庫表結(jié)構(gòu),只會(huì)和數(shù)據(jù)庫中的表進(jìn)行比較,不會(huì)創(chuàng)建新表,但是會(huì)插入新值。

再說點(diǎn)“廢話”:
當(dāng)我們把hibernate.hbm2ddl.auto=create時(shí)hibernate先用hbm2ddl來生成數(shù)據(jù)庫schema。
當(dāng)我們把hibernate.cfg.xml文件中hbm2ddl屬性注釋掉,這樣我們就取消了在啟動(dòng)時(shí)用hbm2ddl來生成數(shù)據(jù)庫schema。通常 只有在不斷重復(fù)進(jìn)行單元測(cè)試的時(shí)候才需要打開它,但再次運(yùn)行hbm2ddl會(huì)把你保存的一切都刪除掉(drop)---- create配置的含義是:“在創(chuàng)建SessionFactory的時(shí)候,從scema中drop掉所以的表,再重新創(chuàng)建它們”。
注意,很多Hibernate新手在這一步會(huì)失敗,我們不時(shí)看到關(guān)于Table not found錯(cuò)誤信息的提問。但是,只要你根據(jù)上面描述的步驟來執(zhí)行,就不會(huì)有這個(gè)問題,因?yàn)閔bm2ddl會(huì)在第一次運(yùn)行的時(shí)候創(chuàng)建數(shù)據(jù)庫schema, 后續(xù)的應(yīng)用程序重啟后還能繼續(xù)使用這個(gè)schema。假若你修改了映射,或者修改了數(shù)據(jù)庫schema,你必須把hbm2ddl重新打開一次。

數(shù)據(jù)操作服務(wù)

當(dāng)前操作數(shù)據(jù)一般都是使用ORM框架,這里以hibernate為例。
代碼較多,先建立幾個(gè)實(shí)體對(duì)象(省略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)建一個(gè)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。只是用了很多注解,做了對(duì)象和表結(jié)構(gòu)的映射。然后繼承CrudRepository。

  • @Entity:注解類,映射成表。注意類要有一個(gè)protect的默認(rèn)構(gòu)造器。
  • @Repository 注解的接口,表示提供對(duì)數(shù)據(jù)庫的操作服務(wù)。同時(shí)spring會(huì)為這個(gè)接口創(chuàng)建對(duì)應(yīng)的bean,以備注入使用。
  • CrudRepository提供了基本的CRUD操作。其他的操做,如findByIsbn。需要根據(jù)命名習(xí)慣,spring會(huì)自動(dòng)解析,如s findByNameIgnoringCase。
  • @Id @GeneratedValue 自增主鍵
  • @ManyToOne @ManyToMany @OneToMany表示兩個(gè)實(shí)體的對(duì)應(yīng)關(guān)系。如book和author是多對(duì)一的關(guān)系。

調(diào)度執(zhí)行器

實(shí)現(xiàn)每10秒執(zhí)行一次。
在BookPubApplication加上@EnableScheduling注解。
在starter加一個(gè)方法

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

原理可以看下,@EnableScheduling注解,它引入SchedulingConfiguration類。這個(gè)類會(huì)創(chuàng)建一個(gè)ScheduledAnnotationBeanPostProcessor。它會(huì)掃描被@Scheduled注解的無參方法。注意方法一定要是無參的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容