Spring Boot快速開發REST服務最佳實踐


一、為什么選擇Spring Boot

Spring Boot是由Pivotal團隊提供的全新框架,被很多業內資深人士認為是可能改變游戲規則的新項目。早期我們搭建一個SSH或者Spring Web應用,需要非常繁瑣的步驟,比如配置web.xml,配置數據庫連接,配置事務,配置日志,配置Tomcat,裝配Bean,聲明和配置切面等等等等,如果項目過大多人協作各種冗長啰嗦的配置讓人煩不勝煩,這么多年下來,給人一種Java就是大型配置文件的感覺。

Spring Boot的設計目的是用來簡化新Spring應用的初始搭建以及開發過程,吸引更多開發者的最大亮點之一是集成了自動配置的魔力。Spring Boot的四個主要新特性如下:

1、Spring Boot Starter:它將常用的依賴分組進行了整合, 將其合並到一個依賴中, 這樣就可以一次性添加到項目的Maven或Gradle構建中;Spring Boot通過提供眾多起步依賴降低項目依賴的復雜度。起步依賴本質上是一個Maven項目對象模型(Project Object Model, POM),定義了對其他庫的傳遞依賴,這些東西加在一起即支持某項功能。很多起步依賴的命名都暗示了它們提供的某種或某類功能。Spring Boot經過了足夠的測試,確保引入的全部依賴都能相互兼容。這是一種解脫,只需指定起步依賴,不用擔心自己需要維護哪些庫,也不必擔心它們的版本。
2、自動配置:Spring Boot的自動配置特性利用了Spring 4對條件化配置的支持, 合理地推測應用所需的bean並自動化配置它們;最后, Spring Boot沒有引入任何形式的代碼生成,而是利用了Spring 4的條件化配置特性,以及Maven和Gradle提供的傳遞依賴解析,以此實現Spring應用程序上下文里的自動配置。簡而言之, Spring Boot的自動配置是一個運行時(更准確地說,是應用程序啟動時)的過程,考慮了眾多因素,才決定Spring配置應該用哪個,不該用哪個。每當應用程序啟動的時候, Spring Boot的自動配置都要做將近200個這樣的決定,涵蓋安全、集成、持久化、 Web開發等諸多方面。所有這些自動配置就是為了盡量不讓你自己寫配置。
3、命令行接口(Command-line interface, CLI):Spring Boot的CLI發揮了Groovy編程語言的優勢, 並結合自動配置進一步簡化Spring應用的開發;
4、Actuator: 它為Spring Boot應用添加了一定的管理特性。

常見的搭建一個Spring Boot應用只要如下幾步:

1、打開http://start.spring.io/

2、點擊“Switch to the full version,選擇Java版本,如1.8,選擇好構建工具,如Maven或Gradle

4、點擊Generate Project下載項目壓縮包

5、解壓后,使用eclipse或者intellij idea,導入項目即可

當然,對於經驗豐富的老鳥,不需要打開網頁再下載解壓導入文件那么多步驟,通過Idea也可以一步一步構造Spring Boot項目。

二、工程結構

官方生成的Spring Boot項目,默認結構說明:

1、src/main/java  程序開發以及主程序入口
2、src/main/resources 配置文件
3、src/test/java  測試程序

src/main/java根目錄下面,有如下Java類和包:

1、Application.java 應用程序入口,包括一個靜態main方法,可以做一些框架配置,比如mybatis、swagger等
2、domain目錄主要用於數據訪問實體(DataObject)與數據訪問層(Repository)
3、service目錄主要是業務邏輯相關的服務接口和實現
4、controller負責頁面訪問控制,對外暴露API

當然,我們需要參考成熟的項目結構或者根據個人經驗來改造項目結構,本文的SpringBootDemo為了方便僅做簡單調整,項目結構如下:

主要包說明:

公共模塊

1、common:公共類,如枚舉,常量、業務無關的通用公共實體等

2、util:常用實用的幫助類,如反射、字符串、集合、枚舉、正則、緩存、隊列等

3、config:自定義的配置項,可從配置文件讀取

表現層

1、controller:負責頁面訪問控制,對外暴露Rest API接口

數據訪問層

1、domain:數據對象實體DO,通常和數據表、視圖或其他業務對象一一對應

2、dao:數據訪問對象,本文demo選擇比較熟悉的mybatis作為ORM工具

業務邏輯層

1、service:服務 contract是接口,impl是服務實現

2、entity:實體 vo是服務可對外公開的實體;dto是數據傳輸對象,可在服務間傳遞;qo:查詢對象,可以認為是查詢條件的封裝

本文demo沒有寫dto和qo示例,很多中小型項目,entity其實是非常混亂的,實體設計和分層抽象有問題,有時候直接影響到業務邏輯復雜程度。

關於工程結構,尤其是應用分層和領域實體抽象(數據訪問對象DO和顯示層對象VO等),強烈推薦大家參考<<阿里巴巴Java開發手冊>>終極版本的工程結構一章。

 

三、MyBatis使用

Maven依賴:

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.2.0</version>
        </dependency>
View Code

數據庫驅動:

MySQL:

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.39</version>
        </dependency>
View Code

SQLServer:

        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>mssql-jdbc</artifactId>
            <version>6.4.0.jre8</version>
        </dependency>
View Code

隆重公告,微軟Microsoft JDBC Driver For SQL Server已發布到maven中央倉庫,本文的demo選擇的是SQLServer,主要是MySQL很多人都寫了,資料太充足,不如試試不同的風格,而且我在前廠寫的第一個線上Spring Boot應用也是訪問SQLServer,demo就哪個好舉例就用哪個了。

配置文件中的配置:

## MYSQL數據源配置
#spring.datasource.url=jdbc:mysql://localhost:3306/springbootdb?useUnicode=true&characterEncoding=utf8
#spring.datasource.username=root
#spring.datasource.password=123456
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver

## MSSQL數據源配置
spring.datasource.url=jdbc:sqlserver://localhost;databaseName=TestDB
spring.datasource.username=sa
spring.datasource.password=123456
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.jpa.show-sql=true
spring.jpa.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
#spring.jpa.hibernate.ddl-auto = create-drop

## Mybatis 配置
mybatis.typeAliasesPackage=com.power.demo.domain
mybatis.mapperLocations=classpath:mapper/*.xml
View Code

Mapper配置:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.power.demo.dao.GoodsDao">
    <resultMap id="BaseResultMap" type="com.power.demo.domain.GoodsDO">
        <result column="GoodsId" property="goodsId"/>
        <result column="GoodsCode" property="goodsCode"/>
        <result column="GoodsName" property="goodsName"/>
        <result column="GoodsType" property="goodsType"/>
        <result column="CreateTime" property="createTime"/>
        <result column="Disabled" property="disabled"/>
    </resultMap>

    <parameterMap id="Goods" type="com.power.demo.domain.GoodsDO"/>

    <sql id="Base_Column_List">
        GoodsId,GoodsCode,GoodsName,GoodsType,CreateTime,Disabled
    </sql>

    <select id="findGoodsByGoodsId" resultType="GoodsDO" parameterType="java.lang.String">
        SELECT
        <include refid="Base_Column_List"/>
        FROM Goods WITH(NOLOCK) WHERE 1=1
        AND Disabled=0
        AND GoodsId = #{goodsId}
    </select>

    <select id="findGoodsByGoodsCode" resultType="GoodsDO" parameterType="java.lang.String">
        SELECT
        <include refid="Base_Column_List"/>
        FROM Goods WITH(NOLOCK) WHERE 1=1
        AND Disabled=0
        AND GoodsCode = #{goodsCode}
    </select>

    <select id="findGoodsByGoodsType" resultType="GoodsDO" parameterType="java.lang.Integer">
        SELECT
        <include refid="Base_Column_List"/>
        FROM Goods WITH(NOLOCK) WHERE 1=1
        AND Disabled=0
        AND GoodsType = #{goodsType}
    </select>

    <select id="findAllGoods" resultType="GoodsDO">
        SELECT
        <include refid="Base_Column_List"/>
        FROM Goods WITH(NOLOCK) WHERE 1=1
    </select>

    <insert id="insertGoods" parameterMap="Goods">

        INSERT INTO Goods (GoodsId,GoodsCode,GoodsName,GoodsType,CreateTime,Disabled)
        VALUES(#{goodsId},#{goodsCode},#{goodsName},#{goodsType},#{createTime},#{disabled})

    </insert>

    <insert id="updateGoods" parameterMap="Goods">

        UPDATE Goods SET Disabled=#{disabled} WHERE GoodsId=#{goodsId}

    </insert>

    <insert id="deleteGoods" parameterType="java.lang.String">

        DELETE FROM Goods WHERE GoodsId=#{goodsId}

    </insert>

</mapper>
View Code

最后,在應用程序入口,添加一行注解:

// mapper 接口類掃描包配置
@MapperScan("com.power.demo.dao")
public class Application {
}

本文demo只提供了簡單的CRUD,但是常見的開發還有很多東西要寫。比如:

如何做多庫配置,如何動態拼接執行復雜SQL,如何批量插入,如何拿到運行時SQL語句,如何使用存儲過程,如何進行數據緩存、如何使用事務、如何級聯查詢等。

這些遺留內容希望有心的你慢慢去發掘嘗試了。

推薦MyBatis代碼生成器:MyBatis Generator

四、API文檔描述

使用應用廣泛的Swagger,生成API文檔。

Maven中添加依賴:

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.2.2</version>
        </dependency>
View Code

需要在應用程序入口Application里面加一行注解:

@EnableSwagger2
public class Application {
}

 不要忘了Api說明和實體說明配置,demo提供了完整示例。

五、單元測試

使用Junit進行單元測試

/**
 * Created by JeffWong.
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class CommonTests {


    @Test
    public void testGoodsTypeEnum() throws Exception {

        GoodsType goodsType = GoodsType.Discount;

        System.out.println(GoodsType.Normal.ordinal());//0
        System.out.println(GoodsType.Discount.ordinal());//1

        System.out.println(goodsType.getCode());
        System.out.println(goodsType.getDescription());

        Assert.isTrue(goodsType.getCode() == 1024, "折扣商品Code為1024");


    }
}
View Code

如果需要單元測試的方法需要配置文件,那么test下也要有resources目錄用於存放資源文件。

本文demo提供了完整的各層單元測試方法,大家可以參考下。

總結:使用Spring Boot全家桶,你可以快速上手開發Java的REST接口應用,配合Java8+的相關新特性,寫Java也越來越省心(雖然Java8的lambda比較難受,Checked Exception層層感染有點受不了,Date非常不好用-_-),聽說Java10要有var,估計還可以少寫很多代碼,而且VS也要支持Java了(不是J#),作為有豐富開發經驗的.NET開發者,也就更加有嘗試的沖動了。

最后提供demo下載

 

參考:

<<阿里巴巴Java開發手冊>>

<<Spring in Action>>

<<Spring Boot in Action>>

https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/

https://www.jianshu.com/u/6a622d516e32

http://spring.io/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM