基於spring boot框架進行二次封裝,微型框架編寫思路

蘇小林發表於2019-04-24

目標:減少重複程式碼,提高開發效率,專案地址:github.com/neatlife/jf…

歡迎star,歡迎pr(求star, 求star, 求star)

可封裝功能列表

E文 功能 目前作用
controller 控制器父類 簡化控制器裡的常見操作
exception 異常 統一全域性業務異常操作
handler 異常攔截 全域性異常攔截到日誌裡
http 請求響應實體 統一全域性響應實體
hystrix hystrix事件攔截 熔斷時傳送報警郵件
jpa jpa非業務重複程式碼封裝 自動給deleted_at, updated_at, created_at賦值
json 響應json資料的二次處理 自動格式化金額小數位數
listener spring boot框架事件 在框架啟動時給util注入service例項
logback 自定義logback日誌輸出 將日誌輸出到elk的redis,方便elk統一進行日誌處理
model 基礎jpa實體 包含deleted_at, updated_at, created_at欄位的實體
redis 自定義redis序列化和反序列化操作 使用GenericJackson2JsonRedisSerializer序列化redis值,提高可讀性
request 自定請求資料的解析 處理json請求
springfox 自定義springfox文件解析 ApiClass支援文件描述引用
util 常用工具收集 比如json, http請求等常用工具類
validator 請求引數自定義校驗 檢查引數長度
apiversion 支援不同的api的版本到不同的控制器邏輯 參考 MainController

目前已經實現上面列表中的所有功能並開源

基於spring boot框架進行二次封裝,微型框架編寫思路

一些關鍵的點

json & http客戶端的封裝

json & http客戶端經常使用,自行編寫容易踩坑,所以直接拿dew框架的工具類進行了整合

github.com/gudaoxuri/d…

需要同時發出多個http請求時可以使用parallelStream()技術,可顯著減少請求總時長

List<String> paramList = ...
paramList.parallelStream().forEach(param -> {
    http post with param
})
複製程式碼

經測試, 26個請求,使用parallelStream將請求總時間從23.803減少到1.731秒

MapperUtil工具類的封裝

因為java裡物件複製非常頻繁,所以封裝了MapperUtil, 雖然複製物件只需下面兩行程式碼

Class1 class1 = new Class1();
BeanUtils.copyProperties(source, class1);
複製程式碼

但是由於非常頻繁的類似程式碼,一個專案專案可能出現上千次這種複製,所以將上面兩行程式碼合併成了一行

Class1 class1 = MapperUtil.to(source, class1);
複製程式碼

核心實現程式碼如下:

public static <SOURCE, TARGET> TARGET to(SOURCE source, Class<TARGET> targetClass) {
    TARGET target = BeanUtils.instantiateClass(targetClass);
    BeanUtils.copyProperties(source, target);
    return target;
}
複製程式碼

雖然使用了反射技術,但經過測試無效能問題

elk日誌

使用redis進行了日誌中轉,所以logstash需要從redis讀取日誌輸出到elasticsearch

logstash從redis讀取日誌輸出到elasticsearch的配置

input {
    redis {
        data_type => "list"
        key => "logstash"
        host => "127.0.0.1"
        port => 6379
        threads => 5
        codec => "json"
    }
}
filter {

}
output {

    elasticsearch {
        hosts => ["elasticsearch:9200"]
        index => "logstash-%{type}-%{+YYYY.MM.dd}"
        document_type => "%{type}"
        workers => 1
        template_overwrite => true
    }
    stdout{}
}
複製程式碼

因為elasticsearch裡日誌會越來越大,所以需要定時清理,定時清理elasticsearch資料

參考:www.jianshu.com/p/458421dac…

核心程式碼如下

#!/bin/bash
# filename:deleteEsData.sh
# 每天2點定時刪除es中指定日期的資料
# crontab: 0 2 * * * sh /home/scripts/deleteEsData.sh >> /home/scripts/logs/deleteEsData.run.log 2>&1
# 如今天是2017-09-21 50天前是2017.08.02
# createdate: 20190921

today=`date +%Y-%m-%d`;
echo "今天是${today}"

# 不指定引數時,預設刪除daynum天前以logs-開頭的資料
daynum=51

# 當引數個數大於1時,提示引數錯誤
if [ $# -gt 1 ] ;then
        echo "要麼不傳引數,要麼只傳1個引數!"
        exit 101;
fi

# 當引數個數為1時,獲取指定的引數
if [ $# == 1 ] ;then
        daynum=$1
fi

esday=`date -d '-'"${daynum}"' day' +%Y.%m.%d`;
echo "${daynum}天前是${esday}"

curl -XDELETE http://127.0.0.1:9200/logs-${esday}
echo "${esday} 的log刪除執行完成"
複製程式碼

全域性業務異常

定義全域性業務異常,方便進行全域性業務異常的捕獲和統一處理 ControllerExceptionHandler

核心處理邏輯如下:

@ExceptionHandler({BusinessRuntimeException.class})
@ResponseBody
public Response handlerBusinessException(BusinessRuntimeException ex) {
    return new Response<Map>(HttpCode.SUCCESS.toString(), ex.getMessage(), Maps.newHashMap());
}
複製程式碼

這樣的好處是方便對錯誤的返回資料進行格式化

利用框架事件,簡化util

RedisUtil工具類需要RedisTemplate操作例項物件,如果在RedisUtil使用Autowired自動注入RedisTemplate,則需要RedisUtil也是Service,這將RedisUtil的呼叫變得複雜化了(使用前必須先注入RedisUtil以獲取RedisTemplate物件) 利用框架事件,可在框架啟動時,獲取RedisTemplate物件,手動注入RedisUtil裡

核心實現程式碼如下:

@Component
public class ApplicationStartedEventListener implements ApplicationListener<ApplicationStartedEvent> {

    private final RedisTemplate redisTemplate;
    private final JFrameworkConfig jFrameworkConfig;

    @Autowired
    public ApplicationStartedEventListener(RedisTemplate redisTemplate, JFrameworkConfig jFrameworkConfig) {
        this.redisTemplate = redisTemplate;
        this.jFrameworkConfig = jFrameworkConfig;
    }

    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        RedisUtil.setRedisTemplate(redisTemplate);
        LockUtil.setRedisTemplate(redisTemplate);
        DingTalkUtil.setDdUrl(jFrameworkConfig.getNotification().getDingTalkUrl());
    }
}
複製程式碼

docker 映象倉庫選擇

自行搭建映象倉庫不如使用免費穩定的來得高效可靠

可以直接使用了aliyun提供的免費docker映象倉庫

自動維護created_at, updated_at, deleted_at欄位

採用了jpa的事件機制,在建立,修改,刪除事件發生時自動進行維護

核心程式碼如下:

@PrePersist
public void touchCreated(BaseEntity target) {
    target.setCreatedAt(DateUtil.currentSecond());
    target.setUpdatedAt(DateUtil.currentSecond());
}

@PreUpdate
public void touchUpdate(BaseEntity target) {
    target.setUpdatedAt(DateUtil.currentSecond());
}

@PreRemove
public void touchDeleted(BaseEntity target) {
    target.setDeletedAt(DateUtil.currentSecond());
}
複製程式碼

ci/cd的選擇

目前ci/cd工具選擇非常多,但在生產裡用得最多的應該是gitlab和jenkins

目前仍然採用jenkins作為ci/cd工具,gitlab的ci目前個人認為仍然不夠成熟

程式碼質量檢測

可以直接試用sonarkube的開放服務,無需自行搭建:

sonarcloud.io/dashboard?i…

打包分發

一般框架會作為專案的基礎依賴,那麼能夠非常方便的下載這個框架就非常重要了

目前nexus私服仍然是首選,可以使用docker搭建nexus私服:

version: "3"

services:
  nexus:
    image: sonatype/nexus3
    environment:
      - INSTALL4J_ADD_VM_PARAMS=-Xms512m -Xmx768m -XX:MaxDirectMemorySize=1g -Djava.util.prefs.userRoot=/nexus-data/javaprefs
    volumes:
      - ./nexus-data:/nexus-data
    ports:
      - "8081:8081"
複製程式碼

微服務

由於是微服務形式的部署,所以日誌,配置都需要中心化,k8s也是標配

日誌選經典的elk

配置中心選擇apollo

部署平臺可以選擇k8s,或者k8s上的istio,目前使用k8s

微服務的k8s模版參考 應用: raw.githubusercontent.com/neatlife/jf… 閘道器: raw.githubusercontent.com/neatlife/jf…

核心區別是kind欄位,閘道器是 DaemonSet 應用是 Deployment

專案參考

完成框架的封裝必然少不了很多參考,即使這是個小框架

以下是參考列表

  1. github.com/kmtong/logb…
  2. github.com/looly/hutoo…
  3. github.com/gudaoxuri/d…
  4. github.com/gudaoxuri/d…
  5. github.com/xdd-organ/x…
  6. github.com/indrabasak/…
  7. github.com/ityouknow/s…
  8. github.com/WellJay/spr…

目前文章還在持續更新中,如果對框架開發感興趣,可加作者微信探討,也可提pr,issue等

suxiaolinKing

相關文章