1. 程式人生 > >基於SpringBoot搭建應用開發框架— 基礎架構

基於SpringBoot搭建應用開發框架— 基礎架構

目錄

回到頂部

Spring的簡史

第一階段:XML配置,在Spring1.x時代,使用Spring開發滿眼都是xml配置的Bean,隨著專案的擴大,我們需要把xml配置檔案分放到不同的配置檔案裡,那時候需要頻繁的在開發的類和配置檔案之間切換。

第二階段:註解配置,在Spring2.x時代,Spring提供宣告Bean的註解,大大減少了配置量。應用的基本配置用xml,業務配置用註解。

第三階段:Java配置,從Spring3.x到現在,Spring提供了Java配置,使用Java配置可以讓你更理解你所配置的Bean。

Spring Boot:使用“習慣優於配置”的理念讓你的專案快速執行起來。使用Spring Boot很容易建立一個獨立執行、準生產級別的基於Spring框架的專案,使用Spring Boot你可以不用或者只需要很少的Spring配置。

下面就來使用Spring Boot一步步搭建一個前後端分離的應用開發框架,並且以後不斷的去完善這個框架,往裡面新增功能。後面以實戰為主,不會介紹太多概念,取而代之的是詳細的操作。

回到頂部

零、開發技術簡介

開發平臺:windows

開發工具:Intellij IDEA 2017.1

JDK:Java 8

Maven:maven-3.3.9

伺服器:tomcat 8.0

資料庫:MySQL 5.7

資料來源:Druid1.1.6

快取:Redis 3.2

日誌框架:SLF4J+Logback

Spring Boot:1.5.9.RELEASE

ORM框架:MyBatis+通用Mapper

回到頂部

一、建立專案

這一節建立專案的基礎結構,按照spring boot的思想,將各個不同的功能按照starter的形式拆分開來,做到靈活組合,並簡單介紹下Spring Boot相關的東西。

1、建立工程

① 通過File > New > Project,新建工程,選擇Spring Initializr,然後Next。

② 儘量為自己的框架想個好點的名字,可以去申請個自己的域名。我這裡專案名稱為Sunny,專案路徑為com.lyyzoo.sunny。

③ 這裡先什麼都不選,後面再去整合。注意我的Spring Boot版本為1.5.9。Next

④ 定義好工程的目錄,用一個專用目錄吧,不要在一個目錄下和其它東西雜在一起。之後點選Finish。

上面說的這麼詳細,只有一個目的,從一個開始就做好規範。

⑤ 生成的專案結構如下,可以自己去看下pom.xml裡的內容。

2、建立Starter

先建立一個core核心、cache快取、security授權認證,其它的後面再整合進去。

跟上面一樣的方式,在Sunny下建立sunny-starter-core、sunny-starter-cache、sunny-starter-security子模組。

這樣分模組後,我們以後需要哪個模組就引入哪個模組即可,如果哪個模組不滿足需求,還可以重寫該模組。

最終的專案結構如下:

3、啟動專案

首先在core模組下來啟動並瞭解SpringBoot專案。

① 在com.lyyzoo.core根目錄下,有一個SunnyStarterCoreApplication,這是SpringBoot的入口類,通常是*Application的命名。

入口類裡有一個main方法,其實就是一個標準的Java應用的入口方法。在main方法中使用SpringApplication.run啟動Spring Boot專案。

然後看看@SpringBootApplication註解,@SpringBootApplication是Spring Boot的核心註解,是一個組合註解。

@EnableAutoConfiguration讓Spring Boot根據類路徑中的jar包依賴為當前專案進行自動配置。

Spring Boot會自動掃描@SpringBootApplication所在類的同級包以及下級包裡的Bean。

② 先啟動專案,這裡可以看到有一個Spring Boot的啟動程式,點選右邊的按鈕啟動專案。看到控制檯Spring的標誌,就算是啟動成功了。

③ 替換預設的banner

可以到http://patorjk.com/software/taag/這個網站生成一個自己專案的banner。建立banner.txt並放到resources根目錄下。

4、Spring Boot 配置

① 配置檔案

Spring Boot使用一個全域性的配置檔案application.properties或application.yaml,放置在src/main/resources目錄下。我們可以在這個全域性配置檔案中對一些預設的配置值進行修改。

然後,需要為不同的環境配置不同的配置檔案,全域性使用application-{profile}.properties指定不同環境配置檔案。

我這裡增加了開發環境(dev)和生產環境(prod)的配置檔案,並通過在application.properties中設定spring.profiles.active=dev來指定當前環境。

② starter pom

Spring Boot為我們提供了簡化開發絕大多數場景的starter pom,只要使用了應用場景所需的starter pom,無需繁雜的配置,就可以得到Spring Boot為我們提供的自動配置的Bean。

後面我們將會通過加入這些starter來一步步整合我們想要的功能。具體有哪些starter,可以到官網檢視:Starters

③ 自動配置

Spring Boot關於自動配置的原始碼在spring-boot-autoconfigure中如下:

我們可以在application.properties中加入debug=true,檢視當前專案中已啟用和未啟用的自動配置。

我們在application.properties中的配置其實就是覆蓋spring-boot-autoconfigure裡的預設配置,比如web相關配置在web包下。

常見的如HttpEncodingProperties配置http編碼,裡面自動配置的編碼為UTF-8。

MultipartProperties,上傳檔案的屬性,設定了上傳最大檔案1M。

ServerProperties,配置內嵌Servlet容器,配置埠、contextPath等等。

之前說@SpringBootApplication是Spring Boot的核心註解,但他的核心功能是由@EnableAutoConfiguration註解提供的。

@EnableAutoConfiguration註解通過@Import匯入配置功能,在AutoConfigurationImportSelector中,通過SpringFactoriesLoader.loadFactoryNames掃描META-INF/spring.factories檔案。

在spring.factories中,配置了需要自動配置的類,我們也可以通過這種方式新增自己的自動配置。

在spring-boot-autoconfigure下就有一個spring.factories,如下:

說了這麼多,只為說明一點,Spring Boot為我們做了很多自動化的配置,搭建快速方便。

但是,正因為它為我們做了很多事情,就有很多坑,有時候,出了問題,我們可能很難找出問題所在,這時候,我們可能就要考慮下是否是自動配置導致的,有可能配置衝突了,或者沒有使用上自定義的配置等等。

5、專案結構劃分

core是專案的核心模組,結構初步規劃如下:

 base是專案的基礎核心,定義一些基礎類,如BaseController、BaseService等;

    cache是快取相關;

    config是配置中心,模組所有的配置放到config裡統一管理;

    constants裡定義系統的常量。

    exception裡封裝一些基礎的異常類;

 system是系統模組;

    util裡則是一些通用工具類;

回到頂部

二、基礎結構功能

1、web支援

只需在pom.xml中加入spring-boot-starter-web的依賴即可。

之後,檢視POM的依賴樹(外掛:Maven Helper),可以看到引入了starter、tomcat、web支援等。可以看出,Sping Boot內嵌了servlet容器,預設tomcat。

自動配置在WebMvcAutoConfiguration和WebMvcProperties裡,可自行檢視原始碼,一般我們不需新增其他配置就可以啟動這個web專案了。

2、基礎功能

在core中新增一些基礎的功能支援。

① 首先引入一些常用的依賴庫,主要是一些常用工具類,方便以後的開發。

 View Code

版本號如下:

② 在base新增一個Result類,作為前端的返回物件,Controller的直接返回物件都是Result。

 View Code

之後在util新增生成Result的工具類Results,用於快速方便的建立Result物件。

 View Code

③ 在base新增BaseEnum<K, V>列舉介面,定義了獲取值和描述的介面。

 View Code

然後在constants下定義一個基礎列舉常量類,我們把一些描述資訊維護到列舉裡面,儘量不要在程式碼中直接出現魔法值(如一些編碼、中文等),以後的列舉常量類也可以按照這種模式來寫。

 View Code

④ 再新增一個常用的日期工具類物件,主要包含一些常用的日期時間格式化,後續可再繼續往裡面新增一些公共方法。

 View Code

⑤ Constants定義系統級的通用常量。

 View Code

⑥ 在base新增空的BaseController、BaseDTO、Service、Mapper,先定義好基礎結構,後面再新增功能。

BaseDTO:標準的who欄位、版本號、及10個擴充套件欄位。

因為這裡用到了@Transient註解,先引入java持久化包:

 View Code

同時,重寫了toString方法,增加了toJsonString方法,使得可以格式化輸出DTO的資料:

直接列印DTO,輸出的格式大概就是這個樣子:

⑦ 在exception新增BaseException,定義一些基礎異常類

基礎異常類都繼承自執行時異常類(RunntimeException),儘可能把受檢異常轉化為非受檢異常,更好的面向介面程式設計,提高程式碼的擴充套件性、穩定性。

BaseException:添加了一個錯誤編碼,其它自定義的異常應當繼承該類。

 View Code

ServiceException:繼承BaseException,Service層往Controller丟擲的異常。

 View Code

3、新增系統使用者功能,使用Postman測試介面

① 在system模組下,再分成dto、controller、service、mapper、constants子包,以後一個模組功能開發就是這樣一個基礎結構。

User:系統使用者

 View Code

UserController:使用者控制層;用@RestController註解,前後端分離,因為無需返回檢視,採用Restful風格,直接返回資料。

 View Code

② Postman請求:請求成功,基礎的HTTP服務已經實現了。

回到頂部

三、整合MyBatis,實現基礎Mapper和Service

1、新增JDBC、配置資料來源

新增spring-boot-starter-jdbc以支援JDBC訪問資料庫,然後新增MySql的JDBC驅動mysql-connector-java;

在application.properties裡配置mysql的資料庫驅動

之後在application-dev.properties裡配置開發環境資料庫的連線資訊,新增之後,Springboot就會自動配置資料來源了。

2、整合MyBatis

MyBatis官方為了方便Springboot整合MyBatis,專門提供了一個符合Springboot規範的starter專案,即mybatis-spring-boot-starter。

在application.properties裡新增mybatis對映配置:

3、新增MyBatis通用Mapper

通用Mapper可以極大的簡化開發,極其方便的進行單表的增刪改查。

關於通用Mapper,參考網站地址:

之後,在core.base下建立自定義的Mapper,按需選擇介面。

 View Code

定義好基礎Mapper後,就具有下圖中的基本通用方法了。每個實體類對應的*Mapper繼承Mapper<T>來獲得基本的增刪改查的通用方法。

在application.properties裡配置自定義的基礎Mapper

4、新增分頁外掛PageHelper

參考地址:

分頁外掛配置,一般情況下,不需要做任何配置。

之後,我們就可以在程式碼中使用 PageHelper.startPage(1, 10) 對緊隨其後的一個查詢進行分頁查詢,非常方便。

5、配置自動掃描Mapper

在config下建立MyBatisConfig配置檔案,通過mapperScannerConfigurer方法配置自動掃描Mapper檔案。

 View Code

注意這裡的 MapperScannerConfigurer 是tk.mybatis.spring.mapper.MapperScannerConfigurer,而不是org.mybatis,否則使用通用Mapper的方法時會報類似下面的這種錯誤

6、定義基礎Service

一般來說,我們不能在Controller中直接訪問Mapper,因此我們需要加上Service,通過Service訪問Mapper。

首先定義基礎Service<T>介面,根據Mapper定義基本的增刪改查介面方法。

 View Code

然後是實現類BaseService,以後的開發中,Service介面實現Service<T>,Service實現類繼承BaseService<T>。

 View Code

BaseService的實現用到了反射工具類Reflections:

 View Code

7、獲取AOP代理

Spring 只要引入aop則是預設開啟事務的,一般我們只要在需要事務管理的地方加上@Transactional註解即可支援事務,一般我們會加在Service的類或者具體的增加、刪除、更改的方法上。

我這裡要說的是獲取代理的問題。Service的事務管理是AOP實現的,AOP的實現用的是JDK動態代理或CGLIB動態代理。所以,如果你想在你的代理方法中以 this 呼叫當前介面的另一個方法,另一個方法的事務是不會起作用的。因為事務的方法是代理物件的,而 this 是當前類物件,不是一個代理物件,自然事務就不會起作用了。這是我在不久前的開發中遇到的實際問題,我自定義了一個註解,加在方法上,使用AspectJ來攔截該註解,卻沒攔截到,原因就是這個方法是被另一個方法以 this 的方式呼叫的,所以AOP不能起作用。

所以新增一個獲取自身代理物件的介面,以方便獲取代理物件來操作當前類方法。Service介面只需要繼承該介面,T為介面本身即可,就可以通過self()獲取自身的代理物件了。

 View Code

還需要開啟開啟 exposeProxy = true,暴露代理物件,否則 AopContext.currentProxy() 會丟擲異常。

8、資料持久化測試

① 實體對映

實體類按照如下規則和資料庫表進行轉換,註解全部是JPA中的註解:

  • 表名預設使用類名,駝峰轉下劃線(只對大寫字母進行處理),如UserInfo預設對應的表名為user_info

  • 表名可以使@Table(name = "tableName")進行指定,對不符合第一條預設規則的可以通過這種方式指定表名。

  • 欄位預設和@Column一樣,都會作為表字段,表字段預設為Java物件的Field名字駝峰轉下劃線形式。

  • 可以使用@Column(name = "fieldName")指定不符合第3條規則的欄位名。

  • 使用@Transient註解可以忽略欄位,新增該註解的欄位不會作為表字段使用,注意,如果沒有與表關聯,一定要用@Transient標註。

  • 建議一定是有一個@Id註解作為主鍵的欄位,可以有多個@Id註解的欄位作為聯合主鍵。

  • 預設情況下,實體類中如果不存在包含@Id註解的欄位,所有的欄位都會作為主鍵欄位進行使用(這種效率極低)。

  • 由於基本型別,如int作為實體類欄位時會有預設值0,而且無法消除,所以實體類中建議不要使用基本型別。

User實體主要加了@Table註解,對映表名;然後在userId上標註主鍵註解;其它欄位如果沒加@Transient註解的預設都會作為表字段。

 View Code

② 建立表結構

 View Code

③ 建立UserMapper

在system.mapper下建立UserMapper介面,繼承Mapper<User>:

 View Code

④ 建立UserService

在system.service下建立UserService介面,只需繼承Service<User>介面即可。

 View Code

在system.service.impl下建立UserServiceImpl實現類,繼承BaseService<User>類,實現UserService介面。同時加上@Service註解。

 View Code

⑤ 修改UserController,注入UserService,增加一些測試API

 View Code

⑥ 測試結果

查詢所有:

批量儲存/修改:

9、程式碼生成器

使用程式碼生成器來生成基礎的程式碼結構,生成DTO、XML等等。

MyBatis官方提供了程式碼生成器MyBatis Generator,但一般需要定製化。MyBatis Generator

我這裡從網上找了一個使用起來比較方便的介面工具,可生成DTO、Mapper、Mapper.xml,生成之後還需做一些小調整。另需要自己建立對應的Service、Controller。之後有時間再重新定製化一個符合本專案的程式碼生成器。

回到頂部

四、日誌及全域性異常處理

在前面的測試中,會發現控制檯輸出的日誌不怎麼友好,有很多日誌也沒有輸出,不便於查詢排查問題。對於一個應用程式來說日誌記錄是必不可少的一部分。線上問題追蹤,基於日誌的業務邏輯統計分析等都離不日誌。

先貼出一些參考資料:

1、日誌框架簡介

Java有很多常用的日誌框架,如Log4j、Log4j 2、Commons Logging、Slf4j、Logback等。有時候你可能會感覺有點混亂,下面簡單介紹下。

  • Log4j:Apache Log4j是一個基於Java的日誌記錄工具,是Apache軟體基金會的一個專案。

  • Log4j 2:Apache Log4j 2是apache開發的一款Log4j的升級產品。

  • Commons Logging:Apache基金會所屬的專案,是一套Java日誌介面。

  • Slf4j:類似於Commons Logging,是一套簡易Java日誌門面,本身並無日誌的實現。(Simple Logging Facade for Java,縮寫Slf4j)。

  • Logback:一套日誌元件的實現(slf4j陣營)。

Commons Logging和Slf4j是日誌門面,提供一個統一的高層介面,為各種loging API提供一個簡單統一的介面。log4j和Logback則是具體的日誌實現方案。可以簡單的理解為介面與介面的實現,呼叫者只需要關注介面而無需關注具體的實現,做到解耦。

比較常用的組合使用方式是Slf4j與Logback組合使用,Commons Logging與Log4j組合使用。

基於下面的一些優點,選用Slf4j+Logback的日誌框架:

  • 更快的執行速度,Logback重寫了內部的實現,在一些關鍵執行路徑上效能提升10倍以上。而且logback不僅效能提升了,初始化記憶體載入也更小了

  • 自動清除舊的日誌歸檔檔案,通過設定TimeBasedRollingPolicy 或者 SizeAndTimeBasedFNATP的 maxHistory 屬性,你就可以控制日誌歸檔檔案的最大數量

  • Logback擁有遠比log4j更豐富的過濾能力,可以不用降低日誌級別而記錄低級別中的日誌。

  • Logback必須配合Slf4j使用。由於Logback和Slf4j是同一個作者,其相容性不言而喻。

  • 預設情況下,Spring Boot會用Logback來記錄日誌,並用INFO級別輸出到控制檯。

2、配置日誌

可以看到,只要集成了spring-boot-starter-web,就引入了spring-boot-starter-logging,即slf4j和logback。

其它的幾個包:jcl-over-slf4j,程式碼直接呼叫common-logging會被橋接到slf4j;jul-to-slf4j,程式碼直接呼叫java.util.logging會被橋接到slf4j;log4j-over-slf4j,程式碼直接呼叫log4j會被橋接到slf4j。

還需引入janino,如果不加入這個包會報錯。

在resources下新增logback.xml配置檔案,Logback預設會查詢classpath下的logback.xml檔案。

具體配置如下,有較詳細的註釋,很容易看懂。可以通過application.properties配置日誌記錄級別、日誌輸出檔案目錄等。

 View Code

加入配置檔案後,就可以看到控制檯格式化後的日誌輸出,還可以看到具體程式碼行數等,比之前的友好多了。

同時,將日誌滾動輸出到日誌檔案,保留歷史記錄。可通過logback.rolling=false控制是否需要輸出日誌到檔案。

3、使用Logger

配置好之後,就可以使用Logger來輸出日誌了,使用起來也是非常方便。

* 可以看到引入的包是slf4j.Logger,程式碼裡並沒有引用任何一個跟 Logback 相關的類,這便是使用 Slf4j的好處,在需要將日誌框架切換為其它日誌框架時,無需改動已有的程式碼。

* LoggerFactory 的 getLogger() 方法接收一個引數,以這個引數決定 logger 的名字,比如第二圖中的日誌輸出。在為 logger 命名時,用類的全限定類名作為 logger name 是最好的策略,這樣能夠追蹤到每一條日誌訊息的來源

* 可以看到,可以通過提供佔位符,以引數化的方式列印日誌,避免字串拼接的不必要損耗,也無需通過logger.isDebugEnabled()這種方式判斷是否需要列印。

4、全域性異常處理

現在有一個問題,當日志級別設定到INFO級別後,只會輸出INFO以上的日誌,如INFO、WARN、ERROR,這沒毛病,問題是,程式中丟擲的異常堆疊(執行時異常)都沒有列印了,不利於排查問題。

而且,在某些情況下,我們在Service中想直接把異常往Controller丟擲不做處理,但我們不能直接把異常資訊輸出到客戶端,這是非常不友好的。

所以,在config下建一個GlobalExceptionConfig作為全域性統一異常處理。主要處理了自定義的ServiceException、AuthorityException、BaseException,以及系統的NoHandlerFoundException和Exception異常。

 View Code

看上面的程式碼,@ControllAdvice(@RestControllerAdvice可以返回ResponseBody),可看做Controller增強器,可以在@ControllerAdvice作用類下新增@ExceptionHandler,@InitBinder,@ModelAttribute註解的方法來增強Controller,都會作用在被 @RequestMapping 註解的方法上。

使用@ExceptionHandler 攔截異常,我們可以通過該註解實現自定義異常處理。在每個處理方法中,封裝Result,返回對應的訊息及狀態碼等。

通過Logger列印對應級別的日誌,也可以看到控制檯及日誌檔案中有異常堆疊的輸出了。注意除了BaseException、Exception,其它的都只是列印了簡單資訊,且為INFO級別。Exception是ERROR級別,且列印了堆疊資訊。

NoHandlerFoundException 是404異常,這裡注意要先關閉DispatcherServlet的NotFound預設異常處理。

測試如下:這種返回結果就比較友好了。

    

回到頂部

五、資料庫樂觀鎖

1、樂觀鎖

在併發修改同一條記錄時,為避免更新丟失,需要加鎖。要麼在應用層加鎖,要麼在快取層加鎖,要麼在資料庫層使用樂觀鎖,使用version作為更新依據【強制】。 —— 《阿里巴巴Java開發手冊》

樂觀鎖,基於資料版本(version)記錄機制實現,為資料庫表增加一個"version"欄位。讀取出資料時,將此版本號一同讀出,之後更新時,對此版本號加一。提交資料時,提交的版本資料與資料庫表對應記錄的當前版本資訊進行比對,如果提交的資料版本號大於資料庫表當前版本號,則予以更新,否則認為是過期資料。

因此,這節就來處理BaseDTO中的"version"欄位,通過增加一個mybatis外掛來實現更新時版本號自動+1。

2、MyBatis外掛介紹

MyBatis 允許在己對映語句執行過程中的某一點進行攔截呼叫。預設情況下, MyBatis 允許使用外掛來攔截的介面和方法包括以下幾個:

  • Executor (update 、query 、flushStatements 、commit 、rollback 、getTransaction 、close 、isClosed)

  • ParameterHandler (getParameterObject 、setParameters)

  • ResultSetHandler (handleResul tSets 、handleCursorResultSets、handleOutputParameters)

  • StatementHandler (prepare 、parameterize 、batch update 、query) 

MyBatis 外掛實現攔截器介面Interceptor,在實現類中對攔截物件和方法進行處理 。 

  • setProperties:傳遞外掛的引數,可以通過引數來改變外掛的行為。

  • plugin:引數 target 就是要攔截的物件,作用就是給被攔截物件生成一個代理物件,並返回。

  • intercept:會覆蓋所攔截物件的原方法,Invocation引數可以反射排程原來物件的方法,可以獲取到很多有用的東西。

除了需要實現攔截器介面外,還需要給實現類配置攔截器簽名。 使用 @Intercepts 和 @Signature 這兩個註解來配置攔截器要攔截的介面的方法,介面方法對應的簽名基本都是固定的。

@Intercepts 註解的屬性是一個 @Signature  陣列,可以在同 一個攔截器中同時攔截不同的介面和方法。

@Signature 註解包含以下三個屬性。

  • type:設定攔截的介面,可選值是前面提到的4個介面 。

  • method:設定攔截介面中的方法名, 可選值是前面4個介面對應的方法,需要和介面匹配 。

  • args:設定攔截方法的引數型別陣列,通過方法名和引數型別可以確定唯一一個方法 。

3、資料版本外掛

要實現版本號自動更新,我們需要在SQL被執行前修改SQL,因此我們需要攔截的就是 StatementHandler  介面的 prepare 方法,該方法會在資料庫執行前被呼叫,優先於當前介面的其它方法而被執行。

在 core.plugin 包下新建一個VersionPlugin外掛,實現Interceptor攔截器介面。

該介面方法簽名如下:

在 interceptor 方法中對 UPDATE 型別的操作,修改原SQL,加入version,修改後的SQL類似下圖,更新時就會自動將version+1。同時帶上version條件,如果該版本號小於資料庫記錄版本號,則不會更新。

VersionInterceptor外掛:

 View Code

之後還需配置該外掛,只需要在MyBatisConfig中加入該配置即可。

最後,如果版本不匹配,更新失敗,需要往外丟擲異常提醒,所以修改BaseService的update方法,增加檢查更新是否失敗。

最後,能不用外掛儘量不要用外掛,因為它將修改MyBatis的底層設計。外掛生成的是層層代理物件的責任鏈模式,通過反射方法執行,會有一定的效能消耗。

我們也可以修改 tk.mapper 生成SQL的方法,加入version,這裡通過外掛方式實現樂觀鎖主要是不為了去修改 mapper 的底層原始碼,比較方便。

回到頂部

六、Druid資料庫連線池

建立資料庫連線是一個很耗時的操作,也很容易對資料庫造成安全隱患。對資料庫連線的管理能顯著影響到整個應用程式的伸縮性和健壯性,影響程式的效能指標。

資料庫連線池負責分配、管理和釋放資料庫連線,它允許應用程式重複使用一個現有的資料庫連線,而不是再重新建立一個;釋放空閒時間超過最大空閒時間的資料庫連線來避免因為沒有釋放資料庫連線而引起的資料庫連線遺漏。資料庫連線池能明顯提高對資料庫操作的效能。

參考:

1、Druid

Druid首先是一個數據庫連線池,但它不僅僅是一個數據庫連線池,它還包含一個ProxyDriver,一系列內建的JDBC元件庫,一個SQLParser。Druid支援所有JDBC相容的資料庫,包括Oracle、MySql、Derby、Postgresql、SQLServer、H2等等。 Druid針對Oracle和MySql做了特別優化,比如Oracle的PSCache記憶體佔用優化,MySql的ping檢測優化。Druid在監控、可擴充套件性、穩定性和效能方面都有明顯的優勢。Druid提供了Filter-Chain模式的擴充套件API,可以自己編寫Filter攔截JDBC中的任何方法,可以在上面做任何事情,比如說效能監控、SQL審計、使用者名稱密碼加密、日誌等等。

2、配置

Druid配置到core模組下,只需在application.properties中新增如下配置即可,大部分配置是預設配置,可更改。有詳細的註釋,比較容易理解。

 View Code

之後啟動專案在位址列輸入/druid/index.html並登入就可以看到Druid監控頁面:

回到頂部

七、Redis快取

對於如今的一箇中小型系統來說,至少也需要一個快取來快取熱點資料,加快資料的訪問資料,這裡選用Redis做快取資料庫。在以後可以使用Redis做分散式快取、做Session共享等。

1、SpringBoot的快取支援

Spring定義了org.springframework.cache.CacheManager和org.springframework.cache.Cache介面來統一不同的快取技術。CacheManager是Spring提供的各種快取技術抽象介面,Cache介面包含快取的各種操作。

針對不同的快取技術,需要實現不同的CacheManager,Redis快取則提供了RedisCacheManager的實現。

我將redis快取功能放到sunny-starter-cache模組下,cache模組下可以有多種快取技術,同時,對於其它專案來說,快取是可插拔的,想用快取直接引入cache模組即可。

首先引入Redis的依賴:

SpringBoot已經預設為我們自動配置了多個CacheManager的實現,在autoconfigure.cache包下。在Spring Boot 環境下,使用快取技術只需在專案中匯入相關的依賴包即可。

在 RedisCacheConfiguration 裡配置了預設的 CacheManager;SpringBoot提供了預設的redis配置,RedisAutoConfiguration 是Redis的自動化配置,比如建立連線池、初始化RedisTemplate等。

2、Redis 配置及宣告式快取支援

Redis 預設配置了 RedisTemplate 和 StringRedisTemplate ,其使用的序列化規則是 JdkSerializationRedisSerializer,快取到redis後,資料都變成了下面這種樣式,非常不易於閱讀。

因此,重新配置RedisTemplate,使用 Jackson2JsonRedisSerializer 來序列化 Key 和 Value。同時,增加HashOperations、ValueOperations等Redis資料結構相關的操作,這樣比較方便使用。

 View Code

同時,使用@EnableCaching開啟宣告式快取支援,這樣就可以使用基於註解的快取技術。註解快取是一個對快取使用的抽象,通過在程式碼中新增下面的一些註解,達到快取的效果。

  • @Cacheable:在方法執行前Spring先檢視快取中是否有資料,如果有資料,則直接返回快取資料;沒有則呼叫方法並將方法返回值放進快取。

  • @CachePut:將方法的返回值放到快取中。

  • @CacheEvict:刪除快取中的資料。

 

Redis伺服器相關的一些配置可在application.properties中進行配置:

3、Redis工具類

新增一個Redis的統一操作工具,主要是對redis的常用資料型別操作類做了一個歸集。

ValueOperations用於操作String型別,HashOperations用於操作hash資料,ListOperations操作List集合,SetOperations操作Set集合,ZSetOperations操作有序集合。

關於redis的key命令和資料型別可參考我的學習筆記:

 View Code

回到頂部

八、Swagger支援API文件

1、Swagger

做前後端分離,前端和後端的唯一聯絡,變成了API介面;API文件變成了前後端開發人員聯絡的紐帶,變得越來越重要,swagger就是一款讓你更好的書寫API文件的框架。

Swagger是一個簡單又強大的能為你的Restful風格的Api生成文件的工具。在專案中整合這個工具,根據我們自己的配置資訊能夠自動為我們生成一個api文件展示頁,可以在瀏覽器中直接訪問檢視專案中的介面資訊,同時也可以測試每個api介面。

2、配置

我這裡直接使用別人已經整合好的swagger-spring-boot-starter,快速方便。

新建一個sunny-starter-swagger模組,做到可插拔。

根據文件,一般只需要做些簡單的配置即可:

但如果想要顯示swagger-ui.html文件展示頁,還必須注入swagger資源:

 View Code

3、使用

一般只需要在Controller加上swagger的註解即可顯示對應的文件資訊,如@Api、@ApiOperation、@ApiParam等。

 View Code

之後訪問swagger-ui.html頁面就可以看到API文件資訊了。

如果不需要swagger,在配置檔案中配置swagger.enabled=false,或移除sunny-starter-swagger的依賴即可。

回到頂部

九、專案優化調整

到這裡,專案最基礎的一些功能就算完成了,但由於前期的一些設計不合理及未考慮周全等因素,對專案做一些調整。並參考《阿里巴巴Java開發手冊》對程式碼做了一些優化。

1、專案結構

目前專案分為5個模組:

最外層的Sunny作為聚合模組負責管理所有子模組,方便統一構建。並且繼承 spring-boot-starter-parent ,其它子模組則繼承該模組,方便統一管理 Spring Boot 及本專案的版本。這裡已經把Spring Boot的版本升到 1.5.10.RELEASE。

 View Code

sunny-starter 則引入了其餘幾個模組,在開發專案時,只需要繼承或引入sunny-starter即可,而無需一個個引入各個模組。

 View Code

對於一個Spring Boot專案,應該只有一個入口,即 @SpringBootApplication 註解的類。經測試,其它的模組的配置檔案application.properties的配置不會生效,應該是引用了入口模組的配置檔案。

所以為了讓各個模組的配置檔案都能生效,只需使用 @PropertySource 引入該配置檔案即可,每個模組都如此。在主模組定義的配置會覆蓋其它模組的配置。

2、開發規範

回到頂部

十、結語

到此,基礎架構篇結束!學習了很多新東西,如Spring Boot、Mapper、Druid;有些知識也深入地學習了,如MyBatis、Redis、日誌框架、Maven等等。

在這期間,看完兩本書,可參考:《MyBatis從入門到精通》、《JavaEE開發的顛覆者 Spring Boot實戰》,另外,開發規範遵從《阿里巴巴Java開發手冊》,其它的參考資料都在文中有體現。