1. 原始碼下載地址
原始碼連結: https://github.com/samt007/xygerp-api-demo
這是用Spring Cloud微服務架構搭建的一套基於EBS的API服務系統
如對本文有任何的疑問,請聯絡我:samt007@qq.com
2. Introduction介紹
這是一篇傳統ERP系統和基於Java的微服務架構有效結合的技術文件。
傳統ERP關注的是企業內部的資訊化管理。當ERP系統能將其服務釋出出去之後(結合微服務架構),就可以很好實現與第三方系統的無縫對接,同時也可以實現擴充套件ERP本身的功能。 目標是:讓ERP的服務更開放!
2.1 它有什麼用?
簡單來說,就是:
相當於做一箇中間服務平臺,把ERP的API做成Web Service與其它系統整合。
下面具體說明它的作用。
1. 如果沒有它...
如果沒有一個統一的API對接平臺,那麼ERP和第三方系統做對接,那會是下圖所示的結構:
從上圖可以看出,各個第三方系統分別和ERP做對接,無論是DBLINK還是通過自己的Web Service, 都是雜亂的,沒能統一管理的。簡單來說就是各自為政,為對接而需要做的事情都是零碎的。
當第三方系統越來越多的時候,那對於日常的運維將會是一個災難的問題。舉個例子,就一個簡單的運維:密碼修改,需要調整的地方就會很多,也很容易遺漏。
特別指出的是,介面功能的複用方面也是個難題。假設一個查詢庫存的介面,CRM系統和線上下單系統都可以用的,需要開發2次。
2. 自從有了它...
有了這套統一的API系統之後,ERP系統和別的系統之間的對接就變成了這個結構:
所以,有了它,相當於ERP的API都可以通過這服務平臺給開發出去,基本上所有的介面可以完成的業務,都可以通過這套服務平臺來完成。
可以實現:
- 對外服務的統一
- API服務之間可以實現互相呼叫,並且實現服務取數和處理的邏輯的統一
- 程式碼的統一,提高開發效率。特別是comm程式碼的部分。
- 提高與第三方系統對接的穩定性:只需要關注該微服務系統的執行穩定性即可。
3. 有哪些例項?
舉個例子:
1) 成品進出條碼管理系統:
大概這個需求: 成品入庫的時候,直接可以用條碼槍掃條碼或者二維碼就可以入庫;銷售出庫的時候,也可以通過刷成品的條碼直接進行出庫。JIT管理。
通過這個系統的實現邏輯是: 通過EBS的使用者名稱和密碼可以登入條碼槍上的APP系統。然後,刷條碼的時候,通過該Web Service可以產生對應的事務處理!例如完工入庫,處理物料搬運單等。
下面是該系統的一些截圖
注意:該功能後臺API由該微服務提供,前臺是安卓的APP
2) 與各個第三方系統的整合:
每個企業內部都有各種第三方系統,這些系統或多或少都需要和EBS進行整合。傳統的整合方法是通過DBLINK。
但是有這套Web Service系統之後,就可以統一通過該Web Service作為中介,和EBS進行資料的整合互動!
2.2 什麼是微服務?
關於它的解析,網上資料很多。 這裡引用某位大神的總結(引用:http://blog.51cto.com/ityouknow/1974080),解析得比較到位:
微服務的概念源於2014年3月Martin Fowler所寫的一篇文章“Microservices”。
微服務架構是一種架構模式,它提倡將單一應用程式劃分成一組小的服務,服務之間互相協調、互相配合,為使用者提供最終價值。每個服務執行在其獨立的程式中,服務與服務間採用輕量級的通訊機制互相溝通(通常是基於HTTP的RESTful API)。每個服務都圍繞著具體業務進行構建,並且能夠被獨立地部署到生產環境、類生產環境等。另外,應儘量避免統一的、集中式的服務管理機制,對具體的一個服務而言,應根據業務上下文,選擇合適的語言、工具對其進行構建。
微服務是一種架構風格,一個大型複雜軟體應用由一個或多個微服務組成。系統中的各個微服務可被獨立部署,各個微服務之間是鬆耦合的。每個微服務僅關注於完成一件任務並很好地完成該任務。在所有情況下,每個任務代表著一個小的業務能力。
微服務架構優勢
複雜度可控:在將應用分解的同時,規避了原本複雜度無止境的積累。每一個微服務專注於單一功能,並通過定義良好的介面清晰表述服務邊界。由於體積小、複雜度低,每個微服務可由一個小規模開發團隊完全掌控,易於保持高可維護性和開發效率。
獨立部署:由於微服務具備獨立的執行程式,所以每個微服務也可以獨立部署。當某個微服務發生變更時無需編譯、部署整個應用。由微服務組成的應用相當於具備一系列可並行的釋出流程,使得釋出更加高效,同時降低對生產環境所造成的風險,最終縮短應用交付週期。
技術選型靈活:微服務架構下,技術選型是去中心化的。每個團隊可以根據自身服務的需求和行業發展的現狀,自由選擇最適合的技術棧。由於每個微服務相對簡單,故需要對技術棧進行升級時所面臨的風險就較低,甚至完全重構一個微服務也是可行的。
容錯:當某一組建發生故障時,在單一程式的傳統架構下,故障很有可能在程式內擴散,形成應用全域性性的不可用。在微服務架構下,故障會被隔離在單個服務中。若設計良好,其他服務可通過重試、平穩退化等機制實現應用層面的容錯。
擴充套件:單塊架構應用也可以實現橫向擴充套件,就是將整個應用完整的複製到不同的節點。當應用的不同元件在擴充套件需求上存在差異時,微服務架構便體現出其靈活性,因為每個服務可以根據實際需求獨立進行擴充套件。
2.3 ERP API微服務系統架構說明
2.3.1 系統開發邏輯說明
從上面的解析得知:微服務是一種技術架構,將一個龐大的服務體系拆分為若干個子服務執行。 問題來了,應該如何拆分呢?就是服務的拆分原則是什麼。
這個問題就像是一個大表如何進行分割槽一樣,其實我覺得是具體問題具體分析。 由於我開發的是基於EBS的微服務系統,正常來說,比較合理的劃分規則應該是以EBS的模組來分。
相當於每個模組都劃分為一個單獨的微服務。例如FND模組,INV模組,WIP模組等等。
有時候,為了某個目的,可能有些功能是定製的,需要提取幾個模組的資料來用,而且被別的模組重用的概率很低。所以,實際上也可以以定製的功能來劃分微服務。
目前來說,該系統包括2個子服務:
- xygerp-ald服務:ald模組
這個是整個微服務API的核心ald模組。這個模組的主要功能是驗證使用者的登入,為所有的api模組提供統一的token認證。相當於ebs的FND模組。
- xygerp-albc服務:albc子模組
這個專案是屬於微服務的API模組之一:條碼管理系統提供資料以及資料處理的API。 主要是為條形碼傳輸系統用。
當然,未來可以新增若干個服務。微服務架構的優勢是有很好的橫向擴充套件能力!
2.3.2 微服務系統架構圖
該系統的架構圖如下所示。
注意:
1.Spring Cloud模組中,實際上Spring Security並不是單獨的一個模組,而是融入到每一個業務微服務模組中! 每個微服務都必須要有token認證才允許訪問API,它非常重要! 所以我將它給列到Spring Cloud模組中。
2.圖中有些模組目前還沒有實現。 目前實現了架構整體,包括以下的服務(下一個章節會具體說明每個模組的用途):
xygerp-ald
xygerp-albc
xygerp-server-eureka
xygerp-server-zuul
注意: 以後會按需新增別的模組。
3 系統開發流程
接下來是一步一步來開發一套這個基於Spring Cloud的微服務系統。
3.1 必須掌握的基礎開發技術知識點
開發系統都必須要打好基礎。所以,這裡列出了開發基於Spring Cloud的微服務系統需要掌握的開發技術。
下面我不會具體解說每一個開發技術如何學習,因為這並不是本文的重點。工欲善其事必先利其器,基礎還是必須要打好。
1)Java語言
必須要熟悉java,否則基本不用看文件了。先打好基礎吧!
2)Maven專案管理工具
系統開發的專案都是以maven做專案管理的,所以必須要先安裝並掌握maven工具。
3)Oracle資料庫+PLSQL+SQL語言
資料庫端的開發技術。這裡選用Oracle資料庫,因為EBS就是基於Oracle資料庫的ERP系統。
3.2 需要熟悉的java框架
Java技術發展到現在,已經出現了許多非常優秀的開源框架,我們可以藉助這些框架來快速開發系統。
3.2.1 Spring框架技術棧(全家桶)
Spring是目前開源的主流的技術包。 該系統主要用到的技術棧是:Spring boot,Spring Security以及Spring Cloud。
- Spring Boot
系統基於SpringBoot快速開發。選擇目前熱度很高的SpringBoot,最大限度地降低配置複雜度,把大量的精力投入到業務開發中來。
- Spring MVC
利用Spring MVC框架處理所有的url請求,簡單易用。
- Spring Security
Spring security是一個強大的和高度可定製的身份驗證和訪問控制框架。它是確保基於Spring的應用程式的標準。 這裡主要是用Spring Security框架處理Token機制。
- Spring Cloud
Spring Cloud是一系列框架的有序集合。 它利用Spring Boot的開發便利性巧妙地簡化了分散式系統基礎設施的開發,如服務發現註冊、配置中心、訊息匯流排、負載均衡、斷路器、資料監控等,都可以用Spring Boot的開發風格做到一鍵啟動和部署。 注意:本系統目前使用Spring Cloud的2個模組
- 請求統一通過API閘道器(Zuul)來訪問內部服務.
- 閘道器接收到請求後,從註冊中心(Eureka)獲取可用服務
3.2.2 MyBatis
ORM框架選用MyBatis。
主要是考慮到它能夠很好支援SQL語句:MyBatis是支援普通SQL查詢,儲存過程和高階對映的優秀持久層框架。 另外,還用到了MyBatis的一些提高開發效率的外掛,特別是通用Mapper和PageHelper分頁外掛!
3.2.3 Alibaba druid
DRUID是阿里巴巴開源平臺上一個資料庫連線池實現。它結合了C3P0、DBCP、PROXOOL等DB池的優點,同時加入了日誌監控,可以很好的監控DB池連線和SQL的執行情況。
3.2.4 Swagger
前端和後端的唯一聯絡,變成了API介面。
API文件變成了前後端開發人員聯絡的紐帶,變得越來越重要,swagger就是一款讓你更好的書寫API文件的框架。
3.3 需要準備的軟體工具
3.3.1Redis
目前用Redis的主要作用是存取token,以配合實現Spring Security完成api訪問的安全機制。
以後可以考慮做快取或者訊息佇列等高階功能。
3.3.2Docker
基於Docker的容器化部署。
由於使用了微服務架構後,我們的系統將會由很多子系統構成。 為了達到多個系統之間環境隔離的目的,我們可以將它們部署在多臺伺服器上,可這樣的成本會比較高,而且每臺伺服器的效能可能都沒有充分利用起來。
所以我們很自然地想到了虛擬機器,在同一臺伺服器上執行多個虛擬機器,從而實現環境的隔離,每個虛擬機器上執行獨立的服務。
然而虛擬機器的隔離成本依舊很高,因為它需要佔用伺服器較多的硬體資源和軟體資源。 所以,在微服務結構下,要實現服務環境的隔離,Docker是最佳選擇。它比虛擬機器更加輕量級,佔用資源較少,而且能夠實現快速部署。
備註:後面有專題說明這個工具如何安裝使用。由於篇幅原因,本文件暫時不講解容器化部署。
3.3.3 Jenkins
Jenkins自動化構建工具。
當我們採用了微服務架構後,我們會發現這樣一個問題。整個系統由許許多多的服務構成,這些服務都需要執行在單獨的容器中,那麼每次釋出的複雜度將非常高。
首先你要搞清楚這些服務之間的依賴關係、啟動的先後順序,然後再將多個子系統挨個編譯、打包、釋出。這些操作技術難度低,卻又容易出錯。
那麼有什麼工具能夠幫助我們解決這些問題呢?答案就是——Jenkins。
它是一款自動化構建的工具,簡單的來說,就是我們只需要在它的介面上按一個按鈕,就可以實現上述一系列複雜的過程。
備註:後面有專題說明這個工具如何安裝使用。由於篇幅原因,本文件暫時不講解自動化構建。
3.4 具體開發流程
現在開始手把手來搭建一套這樣子的系統。
3.4.1 建立Maven專案的組織結構
先建立一個微服務系統的父級專案:xygerp-api
再在這個專案下面分別建立下面幾個子專案:
專案名稱 | 說明 |
---|---|
xygerp-ald | ald模組,埠:8180。這個是整個微服務API的核心ald模組。這個模組的主要功能是驗證使用者的登入,為所有的api模組提供統一的token認證。相當於ebs的FND模組。 |
xygerp-albc | albc子模組,埠:8181。這個專案是屬於微服務的API模組之一:條碼管理系統提供資料以及資料處理的API。主要是為條形碼傳輸系統用。 |
xygerp-comm | comm模組這個專案是所有API專案的核心依賴專案。說白了就是將API微服務架構的所有專案的公用程式碼可以抽取在這裡。 |
xygerp-basic-support | 核心基礎支撐模組這個專案是所有API專案的基礎資料支撐專案。這裡統一歸集了所有的Entity!因為對於Entity來說,應該是整個微服務都公用的。 |
xygerp-server-eureka | Spring Cloud的服務與發現的服務中心。埠:8101。這個模組是Spring cloud的最核心的模組了,用來處理各個微服務之間的服務呼叫的。 |
xygerp-server-zuul | Spring Cloud服務閘道器。埠:8102。在Spring Cloud架構體系內的所有微服務都通過Zuul來對外提供統一的訪問入口,所有需要和微服務架構內部服務進行通訊的請求都走統一閘道器。 |
它們的目錄結果是這樣子的:
注意: 關於xygerp-basic-support:核心基礎支撐模組
可能您會有疑問:為什麼不將entity歸併在它所屬的模組?其實是這樣的,我主要是考慮到服務之間的互相呼叫的問題。
微服務雖然客觀上是一個單獨的服務,但是,實際上大部分的功能肯定是互相呼叫的。舉個例子,銷售訂單模組呼叫庫存模組的功能查詢個庫存是很正常的業務吧?
如果entity不共用的話,相當於銷售模組得到的庫存模組的結果無法歸集為bean來處理,這樣子對於後臺的處理會帶來極大的不便!
3.4.2 構建模組的依賴關係
接著需要通過pom檔案來指定它們之間的依賴關係,依賴關係如下圖所示。
1. 業務服務部分:
注意,上面的4個專案是有依賴關係的。 所以,xygerp-ald和xygerp-albc部署方式 改為war部署 (主要是為了利用jenkins進行自動化部署)。而xygerp-comm和xygerp-basic-support只是為各個微服務提供基礎程式碼支撐,所以是jar部署即可。
此外,為了簡化各個模組的配置,我們將所有模組的通用依賴放在Project的pom檔案中,然後讓所有模組作為Project的子模組。 這樣子模組就可以從父模組中繼承所有的依賴,而不需要自己再配置了。
在父pom中指定子模組
modules標籤指定了當前模組的子模組是誰,但是僅在父模組的pom檔案中指定子模組還不夠,還需要在子模組的pom檔案中指定父模組是誰。
<modules>
<module>xygerp-comm</module> <!--核心Comm模組 -->
<module>xygerp-basic-support</module> <!--核心基礎支撐模組 -->
<module>xygerp-server-eureka</module> <!--Eureka服務治理模組 -->
<module>xygerp-server-zuul</module> <!--zuul動態路由模組 -->
<module>xygerp-ald</module> <!--核心ald模組 -->
<module>xygerp-albc</module> <!--子模組:albc模組,提供條碼對接API服務 -->
</modules>
複製程式碼
需要在子模組中指定父模組
<parent>
<artifactId>xygerp</artifactId>
<groupId>com.xygerp</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
複製程式碼
備註:具體程式碼直接看原始碼吧。這裡只是提及了一些重點設定而已。
所以,到此為止,模組的依賴關係配置完畢!但要注意模組打包的順序。
由於所有模組都依賴於xygerp-comm模組和xygerp-basic-support模組,因此在構建模組時,首先需要編譯、打包、安裝xygerp-comm模組和xygerp-basic-support模組,將它打包進本地倉庫中,這樣上層模組才能引用到。當該模組安裝完畢後,再構建上層模組。 否則在構建上層模組的時候會出現找不到xygerp-comm模組中類庫的問題。
Tips: 其實,如果是在父級目錄直接用mvn package整體打包的話,那打包構建的順序在父pom中是直接指定了!
2. 微服務架構服務治理部分
xygerp-server-eureka:Spring Cloud服務註冊和發現。就是處理服務之間的治理。
xygerp-server-zuul:Spring Cloud的統一API閘道器服務。
Tips: 這2個專案是為了實現微服務架構而用到的核心服務。所以,它們是相對獨立的。不需要依賴父pom。
3.4.3用mvn編譯命令打包程式碼
上面的專案都建立好之後,再新增所有專案都需要用到的依賴(具體程式碼可以參考我的原始碼)。
都沒問題的話,就可以用mvn命令進行打包專案了:
mvn clean install -Dmaven.test.skip=true -P dev
這裡簡單解析一下指令:
mvn:Maven的統一指令。
clean install:表示要構建該專案。
-Dmaven.test.skip=true:表示構建的時候要跳過測試模組。
-P dev:表示構建的時候,啟用 dev 的Spring boot引數執行系統。
如果一切都OK,那正常的結果如下:
3.5 本地電腦測試系統
程式碼搞定了,接下來需要考慮的事情應該是如何測試。 畢竟所有的系統都必須要經過測試,特別是這種配置多,涉及範圍廣的系統。
這個就不得不說一下Spring boot的優勢了。Spring boot的打包應用預設內建了tomcat服務。 換句話說,只需要java命令執行一下Spring boot打包的target結果,就可以啟動一個tomcat服務啦!真挺方便測試的!
3.5.1 啟動本地系統的服務
假設我的xygerp-api專案在這裡:D:\JSP_MyEclipse\xygerp-api
然後分別開啟4個cmd命令視窗,執行:
D:\JSP_MyEclipse\xygerp-api\xygerp-server-eureka\target>java -jar xygerp-server-eureka-1.0-SNAPSHOT.war
D:\JSP_MyEclipse\xygerp-api\xygerp-server-zuul\target>java -jar xygerp-server-zuul-1.0-SNAPSHOT.war
D:\JSP_MyEclipse\xygerp-api\xygerp-ald\target>java -jar xygerp-ald-1.0-SNAPSHOT.war
D:\JSP_MyEclipse\xygerp-api\xygerp-albc\target>java -jar xygerp-albc-1.0-SNAPSHOT.war
複製程式碼
如下圖:
3.5.2 本地測試API服務系統
本地測試環境的服務啟動起來了,接著就是進行具體的資料測試。
首先測試Eureka的服務註冊以及發現,確認服務是否都已經註冊到系統中:
然後,用swagger測試使用者登入的功能: http://127.0.0.1:8102/xygerp/ald/swagger-ui.html 目前是測試是否可以正常產生token。
說明已經登入成功,並且產生了本次訪問的token!將token記錄下來,接著測試。 繼續測試一個查詢的功能: http://127.0.0.1:8102/xygerp/albc/swagger-ui.html
注意,這裡用了Spring Security框架,所以的API請求頭都必須攜帶token資訊。否則請求會返回401。
如果測試OK,那說明基本上系統已經成功搭建好了。 下一步就是如何在測試環境或者正式環境部署它,以及如何一鍵構建專案的問題了。
簡單來說,系統的部署是用 docker工具 ,一鍵部署專案用的是 Jenkins工具。後面將會用專題來說明這2個工具的使用。
3.6 該API微服務系統實現的功能難點
3.6.1 解決資料庫Session的環境變數問題,特別是語言環境和使用者環境。
關於這個問題,目前我用的辦法可能不一定是最優的,如果有別的兄臺有更好的解決辦法,請留言給我,十分感謝!
問題來源:
熟悉EBS開發的兄臺都應該知道,登入ERP之後,我們每次開啟Form,系統就會申請一個新的資料庫Session,這時候,EBS系統會 自動幫我們初始化該Session的環境變數 :例如基本的語言環境,使用者環境,業務實體等等。
這時候,我們在包裡面可以直接用FND_GLOBAL.USER_ID之類的函式就可以非常方便獲取環境變數的資訊。
但是,在Java Web開發裡面就不一樣了!
在Java訪問資料庫的理念中,Session的申請是一個極耗資源的動作!所以,大部分連線資料庫的Java軟體都提出了一個 資料庫連線池 的概念(例如DRUID資料庫連線池)。簡單來說就是session共用!
Session公用就會帶來一個併發問題:A使用者使用系統,並初始化了該Session的環境變數為A使用者;當A使用者不用系統的時候,Session會閒置並放回連線池裡面等待別的使用者使用。
這時候如果B的使用者很可能會使用該Session,如果不重新初始化環境變數的話,那B使用者使用系統的Session的環境變數還是A使用者,就會導致資料的bug! 如何處理該問題是開發該系統碰到的一個難題。
問題解決:
我目前的處理辦法是:在Service層,用AOP統一自動監控Service層的這個引數AuthUser user
只要在Service層將引數AuthUser user放在最後,AOP會自動初始化Session的環境變數。(需要注意的是,我這個系統的資料庫Transaction在Service層啟用!)
另外,語言環境變數,登入ID環境變數等,會一併自動初始化。因為AuthUser會攜帶該定義!
核心處理程式碼如下:
private static final String SQL_GLOBAL_INIT
= " DECLARE "
+ " L_session_id NUMBER;L_user_id NUMBER;L_login_id NUMBER;L_LANG VARCHAR2(10); "
+ " BEGIN "
+ " L_user_id:=:P_USER_ID; L_login_id:=:P_LOGIN_ID; L_LANG:=:P_LANG;"
+ " APPS.fnd_global.INITIALIZE("
+ " session_id=>L_session_id, user_id =>L_user_id, resp_id =>NULL, "
+ " resp_appl_id=>NULL, security_group_id=>NULL, site_id=>NULL, login_id =>L_login_id, "
+ " conc_login_id=>NULL, prog_appl_id=>NULL, conc_program_id=>NULL, conc_request_id=>NULL, "
+ " conc_priority_request=>NULL"
+ " ); "
+ " IF NVL(L_LANG,'US') <> USERENV('LANG') THEN "
+ " IF L_LANG='ZHS' THEN "
+ " APPS.fnd_global.set_nls_context(p_nls_language => 'SIMPLIFIED CHINESE'); "
+ " ELSE "
+ " APPS.fnd_global.set_nls_context(p_nls_language => 'AMERICAN'); "
+ " END IF;"
+ " END IF;"
+ " END; ";
/***
* service層呼叫之前先自動初始化環境變數
* 需要注意的是,使用者變數必須的引數放在最後!
* 只要在Service層將引數AuthUser user放在最後,AOP會自動初始化Session的環境變數。
* @throws Exception
*/
@SuppressWarnings("static-access")
@Before("execution(* com.jebms.*.service..*.*(..)) && args(..,user)")
public void oracleDBInit(JoinPoint joinPoint,AuthUser user) throws Exception{
Long dbLoginId=devDao.getJdbcTemplate().queryForObject("SELECT FND_GLOBAL.LOGIN_ID FROM DUAL", Long.class);
if(user.getLoginId()!=null&&user.getLoginId()>0&&!user.getLoginId().equals(dbLoginId)){
Map<String,Object> inParamMap=new HashMap<String,Object>();
inParamMap.put("P_USER_ID", user.getUserId());
inParamMap.put("P_LOGIN_ID", user.getLoginId());
inParamMap.put("P_LANG", user.getLanguage());
devDao.getDevJdbcTemplate().execute(this.SQL_GLOBAL_INIT, inParamMap);
}
}
複製程式碼
原始碼在:com.jebms.comm.utils. AopUtil
3.6.2 解決EBS的使用者的登入問題:統一用EBS系統的帳號密碼登入API系統。
問題描述:
由於我這個是第三方的API系統,所以,使用者名稱和密碼資訊實際上並不是該API系統需要管理的事情。
相當於說,API系統無法按照正常的流程來驗證使用者名稱和密碼:輸入使用者名稱和密碼,系統驗證後臺資料庫的使用者名稱和密碼,再返回驗證結果。
而是:輸入使用者名稱和密碼,系統 呼叫ERP的使用者名稱密碼驗證包 進行驗證,再返回結果。 簡單來說:需要新增自定義驗證的邏輯。
還好Spring Security框架支援靈活的驗證邏輯。
新增步驟:
首先,寫一個自定義驗證的類:MyAuthenticationProvider
接著,在Spring Security框架的定義中,新增這個自定義的驗證。
AbstractWebSecurityConfig
private MyAuthenticationProvider provider;//自定義驗證 auth.authenticationProvider(provider);
即可以完美實現這個效果
核心程式碼:
/**
* 自定義驗證方式
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
AuthUser user = (AuthUser) userService.loadUserByUsername(username);
System.out.println("username:"+username+",password:"+password);
if(user == null){
throw new BadCredentialsException("Username not found.");
}
//加密過程在這裡體現
if (!sysService.xygErpValidateLogin(username, password)) {
throw new BadCredentialsException("Wrong password.");
}
user.setPassword(passwordEncoder.encode(password));
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
return new UsernamePasswordAuthenticationToken(user, password, authorities);
}
複製程式碼
3.6.3 統一的開發風格。
1.Entity基類封裝。
封裝了5who欄位,以及類似Form的FND_SET_WHO的方法,可以很方便進行開發。
另外,為了防止丟失更新,這邊每次更新前實際上會先檢測資料的一致性,對應的動作也有做了封裝。
2.查詢邏輯的封裝。
查詢功能相對來說還是會很多,對於複雜的查詢條件如何傳值是一個難題。
這裡封裝了一個SearchInfo積累,可以統一將所有的查詢條件都放在這個類,然後在java的Controller層定義好查詢條件對應匹配欄位,系統就可以自動產生對應的and條件。
例如:
@GetMapping(value = "/getPageLocator")
@ApiOperation(value = "貨位分頁列表介面")
public ResultEntity<PageInfo<EslipLocatorRE>> getPageLocator(
@ApiParam(value = "庫存組織ID",required = true) @RequestParam(required = true) int organizationId,
@ApiParam(value = "庫別程式碼",required = true) @RequestParam(required = true) String subinventoryCode,
@ApiParam(value = "貨位程式碼",required = false) @RequestParam(required = false) String locatorCode,
SearchInfo searchInfo) throws Exception {
searchInfo.getConditionMap().put("organizationId", organizationId);
searchInfo.getConditionMap().put("subinventoryCode", subinventoryCode);
searchInfo.getConditionMap().put("locatorCode", locatorCode);
searchInfo.setAuthUser(this.authUser);
searchInfo.initSqlCondition();
searchInfo.andSqlCondition("MIL.ORGANIZATION_ID","organizationId");
searchInfo.andSqlCondition("MIL.SUBINVENTORY_CODE","subinventoryCode");
searchInfo.andSqlCondition("MIL.SEGMENT1","locatorCode");
return eslipService.selectForPageLocator(searchInfo);
}
複製程式碼
3.統一的處理結果的封裝。
基本上任何一個處理,要不成功,要不失敗(警告其實也算失敗)。
這裡封裝了一個返回結果的基類ResultEntity<T>,可以進行有效的應用端或者java端的互動。
@ApiModelProperty(value = "狀態碼,0表示成功 其他表示失敗", example = "0",position = 1)
private String code;
複製程式碼
特別需要指出的是,前端獲取或者處理資料,也是統一要用這個處理結果基類的返回。
簡單來說,就是資料處理成功/失敗,會有一個統一的返回結果標識。注意,這個標識和請求的響應結果標識(200)是有所不同的!
請求響應標識只是說明web伺服器的響應是正常,但,具體的處理結果可能是處理失敗。
下面是一個具體的例子(到時候實際開發處理的介面處理結果也是這樣子):
{
"code": "0",
"message": "",
"description": "",
"obj": [{
"createdBy": -1,
"creationDate": "2017-10-10 09:37:03",
"lastUpdatedBy": 10,
"lastUpdateDate": "2017-11-16 14:47:48",
"lastUpdateLogin": 96,
"valueUUID": null,
"id": 2,
"applId": 1,
"respCode": "BASIC_SET",
"menuId": 2,
"startDate": "2017-10-10 09:37:03",
"endDate": null,
"respName": "系統設定職責",
"description": "系統設定職責",
"menuCode": "SYSTEM_SET",
"menuName": "系統設定選單",
"enabled": true
}],
"param1": null,
"param2": null,
"param3": null,
"param4": null,
"param5": null,
"ok": true
}
複製程式碼
文件參考連結: https://juejin.im/entry/5a7812906fb9a0635014f19a http://blog.51cto.com/ityouknow/1974080