如何基於Spring Boot搭建一個完整的項目


前言

使用Spring Boot做后台項目開發也快半年了,由於之前有過基於Spring開發的項目經驗,相比之下覺得Spring Boot就是天堂,開箱即用來形容是絕不為過的。在沒有接觸Spring Boot 之前,以為Spring Boot 是一個新的框架體系。正好Spring Boot出現先的時候,也是微服務特別火的時候,大家不約而同把Spring Boot 和微服務等同起來了,但其實並不是如此,那么到底什么是Spring Boot ? 基於Spring Boot 又是如何開發的呢 ? 帶着這兩個問題,大家繼續往下看。

Spring Boot 特點

Our primary goals are:

  1. Provide a radically faster and widely accessible getting started experience for all Spring development.
  2. Be opinionated out of the box, but get out of the way quickly as requirements start to diverge from the defaults.
  3. 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 ,東西太多,大家一起加油!


免責聲明!

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



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