前言
使用Spring Boot做后台項目開發也快半年了,由於之前有過基於Spring開發的項目經驗,相比之下覺得Spring Boot就是天堂,開箱即用來形容是絕不為過的。在沒有接觸Spring Boot 之前,以為Spring Boot 是一個新的框架體系。正好Spring Boot出現先的時候,也是微服務特別火的時候,大家不約而同把Spring Boot 和微服務等同起來了,但其實並不是如此,那么到底什么是Spring Boot ? 基於Spring Boot 又是如何開發的呢 ? 帶着這兩個問題,大家繼續往下看。
Spring Boot 特點
Our primary goals are:
- Provide a radically faster and widely accessible getting started experience for all Spring development.
- Be opinionated out of the box, but get out of the way quickly as requirements start to diverge from the defaults.
- Absolutely no code generation and no requirement for XML configuration.
上述是從Spring Boot官方文檔上摘錄的,簡述言之Spring Boot框架的目的就是開箱即用、去除配置。Spring Boot並不是重復去造就一個輪子,它的出現是為Spring的開發帶來一種全新的體驗,從而大大的降低Spring框架的使用門檻。
下面主要從幾個特性介紹Spring Boot是如何簡化開發的。
Spring Boot啟動器
使用過Spring Boot的同學都知道,快速編寫一個Spring Boot的Web Demo需要在pom文件中加入如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
Spring Boot 提供了很多像spring-boot-starter-web這樣的啟動器,名稱都是spring-boot-starter-* 。

截圖部分來自Spring Boot官網,只是Spring Boot啟動器中小部分,更多啟動器可以到官網上查找。
啟動器為何物? 以spring-boot-starter-web 這個web開發用到的啟動器為例。

從上圖可以看出spring-boot-starter-web.jar包里面沒有任何代碼,對沒有任何代碼。只有一個pom文件。what ? 其中包中pom.xml的內容如下所示:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
</dependencies>
以前在基於Spring框架下進行web開發通常需要在模塊的pom文件中引入spring-webmvc、spring-web、jackson、tomcat等依賴模塊。如果使用Spring Boot的啟動器,只需要引入spring-boot-starter-web模塊就可以。看到這里聰明的讀者應該明白了為嘛啟動器的包中沒有代碼只有pom文件了吧。 簡言之Spring Boot的啟動器就是將不同的第三方組件依賴進行了套件封裝,為開發Spring應用提供了一個易用的套件庫。開發者再也不用在pom中引入那么多模塊了,一個套件搞定一個相關的功能所需要的所有依賴庫,這個體驗很棒吧。
繼續解鎖下一個Spring Boot的牛逼特性。
內嵌Web容器(Tomcat或者Jetty)
早期基於Spring框架開發的時候,Web容器是不可以缺少的一個步驟。通常把代碼編譯成war包,然后扔到tomcat容器中進行運行。 這時候需要單獨的下載tomcat容器,有點小麻煩。Spring Boot直接把通過內嵌web容器,直接運行。 那么Spring Boot是如何內嵌的web容器的呢? 其中以tomcat容易為例。
上面介紹啟動器的時候,有介紹spring-boot-starter-web啟動器,這是進行web開發的時候需要在pom中引入的模塊。進入到這個啟動器的內部pom文件,如下描述:
<description>
Starter for building web, including RESTful, applications using
Spring MVC. Uses Tomcat as the default embedded container
</description>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
看了spring-boot-starter-web啟動器的內部pom描述,很容易明白。tomcat是通過spring-boot-starter-web啟動器引入的,並且在Spring Boot 中默認的Web容器是tomcat,當然也可以換成更輕量級的Jetty,這里不做介紹 。關鍵在於spring-boot-starter-tomcat 這個tomcat啟動器,引入它之后,所有tomcat相關的jar包都引入進來了。在啟動Spring Boot服務的時候,就會調用tomcat容器的啟動。為了提高點文章的逼格來點源碼分析。
在啟動Spring Boot的時候,控制台的日志打印其實很好的告知了我們Spring Boot在啟動的時候,框架做了哪些事情。選出兩條和Tomcat相關的日志:
s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8094 (http)
s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8094 (http)
通過日志發現了一個類 TomcatEmbeddedServletContainer,搜尋到這個類屬spring-boot.jar包里面的。通過下圖所示的spring-boot-1.5.9.RELEASE.jar包目錄結構和包名很容易猜測到Web容器的啟動就是這個包來控制的。

核心接口 EmbeddedServletContainer,其中三個容器分別對接口進行了實現,對方法進行了重寫。 接口代碼如下:
public interface EmbeddedServletContainer {
/**
* Starts the embedded servlet container.
*/
void start() throws EmbeddedServletContainerException;
/**
* Stops the embedded servlet container
*/
void stop() throws EmbeddedServletContainerException;
/**
* Return the port this server is listening on.
*/
int getPort();
}
其實除了內嵌web容器的啟動和spring-boot包有關外,整個Spring Boot的服務啟動,都是在這個包中實現的,感興趣的同學可以跟這個包死磕到底,深入下去。
完全去配置文件化
Spring Boot之所以號稱降低了Spring的開發門檻其實是有殺手鐧的,完全去配置化就是重量級的殺手鐧。 早起完成Spring的開發,最煩的就是各種xml配置文件。在Spring Boot的項目工程中確實看不到了那些煩人的xml,它們到哪去了?
回到Spring Boot的項目工程,其中還有兩處變化的沒有介紹。
- Resource資源文件下的四個屬性文件。application.properties 、application-dev.properties、application-prd.properties、application-stg.properties。
- spring-boot-autoconfigure-*.jar(省去了版本號).
屬性文件這里就不介紹了,配置相關的屬性的地方,根據不同的環境(測試、開發、生產)提供了不同名稱的屬性文件。
spring-boot-autoconfigure-*.jar 很明顯從名字就知道,自動配置相關的jar包。沒錯,Spring Boot的屬性自動配置全部都是由這個包實現的。

打開spring-boot-autoconfigure-*.jar,包名稱就告訴了我們一切。在這個包中集成了我們大部分平時開發需要的中間件,比方說: amqp 消息中間件、cache緩存中間件、data數據持久化相關的中間件。基於Spring的框架,如果要集成各種常用中間件, 少不了一個使用了 @Configuration注解的配置類和各種屬性配置。但是使用Spring Boot框架,@Configuration注解的配置類,全部不需要了。只要在application.properties 配置屬性值就可以直接使用。
這里把集成Redis作為一個例子。查看spring-boot-autoconfigure-*.jar 中data包目錄下的redis包可以看到幾個類,其中重點在於RedisAutoConfiguration 和 RedisProperties 這兩個類。
- RedisProperties類

通過 @ConfigurationProperties(prefix = "spring.redis")注解,把在application.properties 中配置的redis 相關的名稱為spring.redis.*的屬性全部注入到 RedisProperties類的屬性變量中。
- RedisAutoConfiguration類
在這個類中主要定義了幾個Bean, 主要是JedisConnectionFactory 和 JedisPoolConfig、StringRedisTemplate等。以前我們基於Spring框架,需要自己定義Bean全部都被Spring Boot框架自己定義了,其他的中間件也是如此。 再結合Spring Boot中使用的是3.0+版本的Servlet包,使用注解完成各種Listener、Filter、Servlet 以及Bean的定義,讓無xml化不是夢了。
思考: 如何修改基於框架下的自動配置的屬性值?
完全依賴框架的自動配置,其實也是件蠻不安全的事情。有次生產環境上,redis一直拋出一個異常。但是沒多久又好了,持續了很多天。從異常可以判斷是客戶端使用了redis連接池提供的無效鏈接。通過Jedis的源碼可以知道,使用的連接池是apache-common-pool2組件。其中相關的參數有BaseObjectPoolConfig類中的
private boolean testOnBorrow = false;
private boolean testWhileIdle = false;
這兩個參數值默認都是false。其中testOnBorrow 代指獲取連接池的連接前,檢查鏈接的有效性。 testWhileIdle 是空閑的時候檢查鏈接的有效性。 需要把這兩個參數都設置為true。因為默認的自動配置值都是false,但是這兩個屬性值又不在RedisProperties類中,所以不能通過application.properties文件來修改。那怎么修改呢? 現在項目重寫了JedisConnectionFactory 和 JedisPoolConfig、StringRedisTemplate這三個Bean,通過重寫Bean來修改屬性值。異常沒有了,但是總覺得這不是最好的方式,大家如果有更好的方式,歡迎留言,謝謝!
等等: 說好的是工程介紹,怎么全是文字,自己都不想看了。好吧,回到正題,搭建基於Spring Boot的工程項目需要哪些東西?
整個項目的結構
<modules>
<module>pa-market-update</module>
<module>pa-market-delete</module>
<module>pa-market-common</module>
<module>pa-market-add</module>
<module>pa-market-schedule</module>
</modules>
完整的項目一般都是根據業務進行功能划分的。上面是主pom文件的內容,common是公共模塊,schedule是定時任務模塊(每個項目應該都少不了定時任務吧),其他的各自業務模塊,名字隨便取的。
單個模塊下包目錄結構

在前后端分離的項目中,通常要求基於Restful風格編寫接口,基於這種風格,項目每個子模塊的包目錄結構其實是可以固定。
- aspect 包: 基於AOP的攔截器一般都放在這個包。
- consts包 : 常量包
- dto包:表現層和業務層的中間產物
- entity包: 對應表結構的實體類
- service包: 業務層,主要實現業務邏輯的Service類
- utils包: 工具類
- web包: Controller控制層,也是一個項目的入口,通過它可以知道有多少接口
- Application 類: Spring Boot的啟動類,注意它放的位置是有講究的
集成Mybatis框架
DAO層在一個項目中是必不可以少的,在這里采用常用的ORM框架Mybatis。 在pom文件中需要加入的模塊。
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
mybatis-spring-boot-starter這個啟動器不是Spring Boot官方提供的,是Mybatis團隊自己集成的,所以名字跟官方的啟動器名字不一樣。
再加上 @Mapper注解的Mapper類和Mapper.xml文件,就可以了。當然少不了在application.properties中配置數據庫的基本信息。使用ORM框架就這么簡單。其他第三方框架也是如此,這里就不重復了。
日志框架
日志打印是一個項目必不可少的部分,在這里介紹log4j2的日志框架。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
log4j2不支持屬性文件,只支持xml文件或者json文件。這里介紹xml文件的配置。
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="INFO" packages="com.pa.market" >
<appenders>
<!--這個輸出控制台的配置 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss} %-5p - %msg%n" />
<!-- 設置級別 -->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="NEUTRAL" />
<ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY" />
<ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/>
</Console>
<RollingFile name="RollingFile" fileName="/data/logs/market/market-inquiry.log"
filePattern="/data/logs/market/market-inquiry.%d{yyyy-MM-dd}.%i.log">
<PatternLayout>
<charset>UTF-8</charset>
<Pattern>%d{HH:mm:ss} %-5p - %msg%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="256 MB" />
</Policies>
</RollingFile>
</appenders>
<loggers>
<!-- 將業務dao接口填寫進去,並用控制台輸出即可 -->
<logger name="com.pa.market.common.mapper" level="DEBUG" additivity="false">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFile" />
</logger>
<AsyncRoot level="info">
<appender-ref ref="Console" />
<appender-ref ref="RollingFile" />
</AsyncRoot>
</loggers>
</configuration>
如果需要打印Mybatis框架中的日志,可以配置 loggers 標簽。每個標簽的含義,在官網上可以查詢到,這里不做介紹。
打包部署
Spring Boot 框架下如果想打包相應的模塊代碼,需要在資源文件下添加一個assembly.xml文件。文件內容如下:
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>release</id>
<formats>
<format>tar.gz</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.basedir}\src\main\resources</directory>
<outputDirectory>conf</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.basedir}\src\script</directory>
<outputDirectory>script</outputDirectory>
<fileMode>0755</fileMode>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<useProjectArtifact>true</useProjectArtifact>
<outputDirectory>lib</outputDirectory> <!-- 將scope為runtime的依賴包打包到lib目錄下。 -->
<scope>runtime</scope>
</dependencySet>
</dependencySets>
</assembly>
通過這個xml文件,maven執行mvn package命令的時候,會出現一個release包,其中包含conf、lib兩個文件夾。其中conf中存放的是模塊下Resource目錄下存放的資源文件,lib包下是模塊所有依賴的jar包。在部署到linux服務器的時候,這兩個文件很重要。
maven的很多功能是通過插件來實現的,所以基於assembly.xml的打包功能,需要在pom文件中配置如下插件。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<excludes>
<exclude>*</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions> <!--執行器 mvn assembly:assembly -->
<execution>
<id>make-zip</id><!--名字任意 -->
<phase>package</phase><!-- 綁定到package生命周期階段上 -->
<goals>
<goal>single</goal><!-- 只運行一次 -->
</goals>
<configuration>
<descriptors> <!--描述文件路徑 -->
<descriptor>src/main/resources/assembly.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
總結
通過后面介紹如何搭建工程的大篇幅粘貼,終於把文章的篇幅給填充上去了, 哈哈,這里再多嘮叨幾句。Spring Boot 框架的出現,讓Spring 開發變得異常的簡單,對於開發者來說是好事也是壞事。好處,開發門檻低了,可以更多的關注業務代碼的書寫。壞處,框架封的越簡單,框架層面出現問題后越傻眼,對底層的框架原理的理解也越少。所以作為有技術追求的你,懂得應該怎么做吧。小編在使用框架的時候,遇到了不少疑問,大部分也都記錄下來了。待把疑問解決后,后續會和大家繼續分享,敬請期待! 從Spring 到 Spring Boot 再到 Spring Cloud ,東西太多,大家一起加油!
