基于Spring Boot的API、RESTful API 項目骨架

      網(wǎng)友投稿 709 2025-03-31

      1?基于spring boot的API、RESTful API 項目骨架

      最近使用spring boot 配合 MyBatis 、通用Mapper插件、PageHelper分頁插件 連做了幾個中小型API項目,做下來覺得這套框架、工具搭配起來開發(fā)這種項目確實非常舒服,團隊的反響也不錯。在項目搭建和開發(fā)的過程中也總結(jié)了一些小經(jīng)驗,與大家分享一下。

      在開發(fā)一個API項目之前,搭建項目、引入依賴、配置框架這些基礎(chǔ)活自然不用多說,通常為了加快項目的開發(fā)進度(早點回家)還需要封裝一些常用的類和工具,比如統(tǒng)一的響應(yīng)結(jié)果封裝、統(tǒng)一的異常處理、接口簽名認證、基礎(chǔ)的增刪改差方法封裝、基礎(chǔ)代碼生成工具等等,有了這些項目才能開工。

      然而,下次再做類似的項目上述那些步驟可能還要搞一遍,雖然通常是拿過來改改,但是還是比較浪費時間。所以,可以利用面向?qū)ο蟪橄?、封裝的思想,抽取這類項目的共同之處封裝成了一個種子項目(估計大部分公司都會有很多類似的種子項目),這樣的話下次再開發(fā)類似的項目直接在該種子項目上迭代就可以了,減少無意義的重復(fù)工作。

      在相關(guān)項目上線之后,我花了點時間對該種子項目做了一些精簡,并且已經(jīng)把該項目分享到GitHub上面了,如果你正準(zhǔn)備做類似項目的話,可以去克隆下來試試。

      項目地址&使用文檔:https://github.com/lihengming/spring-boot-api-project-seed 。

      如果在使用中發(fā)現(xiàn)問題或者有什么好建議的話歡迎提issue或pr一起來完善它。

      特征&提供

      最佳實踐的項目結(jié)構(gòu)、配置文件、精簡的POM

      注:使用代碼生成器生成代碼后會創(chuàng)建model、dao、service、web等包。

      統(tǒng)一響應(yīng)結(jié)果封裝及生成工具

      /**

      * 統(tǒng)一API響應(yīng)結(jié)果封裝

      */

      public class Result {

      private int code;

      private String message;

      private Object data;

      public Result setCode(ResultCode resultCode) {

      this.code = resultCode.code;

      return this;

      }

      //省略getter、setter方法

      }

      /**

      * 響應(yīng)碼枚舉,參考HTTP狀態(tài)碼的語義

      */

      public enum ResultCode {

      基于Spring Boot的API、RESTful API 項目骨架

      SUCCESS(200),//成功

      FAIL(400),//失敗

      UNAUTHORIZED(401),//未認證(簽名錯誤)

      NOT_FOUND(404),//接口不存在

      INTERNAL_SERVER_ERROR(500);//服務(wù)器內(nèi)部錯誤

      public int code;

      ResultCode(int code) {

      this.code = code;

      }

      }

      /**

      * 響應(yīng)結(jié)果生成工具

      */

      public class ResultGenerator {

      private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS";

      public static Result genSuccessResult() {

      return new Result()

      .setCode(ResultCode.SUCCESS)

      .setMessage(DEFAULT_SUCCESS_MESSAGE);

      }

      public static Result genSuccessResult(Object data) {

      return new Result()

      .setCode(ResultCode.SUCCESS)

      .setMessage(DEFAULT_SUCCESS_MESSAGE)

      .setData(data);

      }

      public static Result genFailResult(String message) {

      return new Result()

      .setCode(ResultCode.FAIL)

      .setMessage(message);

      }

      }

      統(tǒng)一異常處理

      public void configureHandlerExceptionResolvers(List exceptionResolvers) {

      exceptionResolvers.add(new HandlerExceptionResolver() {

      public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {

      Result result = new Result();

      if (e instanceof ServiceException) {//業(yè)務(wù)失敗的異常,如“賬號或密碼錯誤”

      result.setCode(ResultCode.FAIL).setMessage(e.getMessage());

      logger.info(e.getMessage());

      } else if (e instanceof NoHandlerFoundException) {

      result.setCode(ResultCode.NOT_FOUND).setMessage("接口 [" + request.getRequestURI() + "] 不存在");

      } else if (e instanceof ServletException) {

      result.setCode(ResultCode.FAIL).setMessage(e.getMessage());

      } else {

      result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + request.getRequestURI() + "] 內(nèi)部錯誤,請聯(lián)系管理員");

      String message;

      if (handler instanceof HandlerMethod) {

      HandlerMethod handlerMethod = (HandlerMethod) handler;

      message = String.format("接口 [%s] 出現(xiàn)異常,方法:%s.%s,異常摘要:%s",

      request.getRequestURI(),

      handlerMethod.getBean().getClass().getName(),

      handlerMethod.getMethod().getName(),

      e.getMessage());

      } else {

      message = e.getMessage();

      }

      logger.error(message, e);

      }

      responseResult(response, result);

      return new ModelAndView();

      }

      });

      }

      常用基礎(chǔ)方法抽象封裝

      public interface Service {

      void save(T model);//持久化

      void save(List models);//批量持久化

      void deleteById(Integer id);//通過主鍵刪除

      void deleteByIds(String ids);//批量刪除 eg:ids -> “1,2,3,4”

      void update(T model);//更新

      T findById(Integer id);//通過ID查找

      T findBy(String fieldName, Object value) throws TooManyResultsException; //通過Model中某個成員變量名稱(非數(shù)據(jù)表中column的名稱)查找,value需符合unique約束

      List findByIds(String ids);//通過多個ID查找//eg:ids -> “1,2,3,4”

      List findByCondition(Condition condition);//根據(jù)條件查找

      List findAll();//獲取所有

      }

      提供代碼生成器來生成基礎(chǔ)代碼

      public abstract class CodeGenerator {

      ...

      public static void main(String[] args) {

      genCode("輸入表名");

      }

      public static void genCode(String... tableNames) {

      for (String tableName : tableNames) {

      //根據(jù)需求生成,不需要的注掉,模板有問題的話可以自己修改。

      genModelAndMapper(tableName);

      genService(tableName);

      genController(tableName);

      }

      }

      ...

      }

      CodeGenerator 可根據(jù)表名生成對應(yīng)的Model、Mapper、MapperXML、Service、ServiceImpl、Controller(默認提供POST和RESTful兩套Controller模板,根據(jù)需要在 genController(tableName)方法中自己選擇,默認是純POST的),代碼模板可根據(jù)實際項目的需求來定制,以便漸少重復(fù)勞動。

      由于每個公司業(yè)務(wù)都不太一樣,所以只提供了一些簡單的通用方法模板,主要是提供一個思路來減少重復(fù)代碼的編寫。在我們公司的實際使用中,其實根據(jù)業(yè)務(wù)的抽象編寫了大量的代碼模板。擴展:優(yōu)秀的代碼都是如何分層的?

      提供簡單的接口簽名認證

      public void addInterceptors(InterceptorRegistry registry) {

      //接口簽名認證-,該簽名認證比較簡單,實際項目中可以使用Json Web Token或其他更好的方式替代。

      if (!"dev".equals(env)) { //開發(fā)環(huán)境忽略簽名認證

      registry.addInterceptor(new HandlerInterceptorAdapter() {

      @Override

      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

      //驗證簽名

      boolean pass = validateSign(request);

      if (pass) {

      return true;

      } else {

      logger.warn("簽名認證失敗,請求接口:{},請求IP:{},請求參數(shù):{}",

      request.getRequestURI(), getIpAddress(request), JSON.toJSONString(request.getParameterMap()));

      Result result = new Result();

      result.setCode(ResultCode.UNAUTHORIZED).setMessage("簽名認證失敗");

      responseResult(response, result);

      return false;

      }

      }

      });

      }

      }

      /**

      * 一個簡單的簽名認證,規(guī)則:

      * 1. 將請求參數(shù)按ascii碼排序

      * 2. 拼接為a=value&b=value...這樣的字符串(不包含sign)

      * 3. 混合密鑰(secret)進行md5獲得簽名,與請求的簽名進行比較

      */

      private boolean validateSign(HttpServletRequest request) {

      String requestSign = request.getParameter("sign");//獲得請求簽名,如sign=19e907700db7ad91318424a97c54ed57

      if (StringUtils.isEmpty(requestSign)) {

      return false;

      }

      List keys = new ArrayList(request.getParameterMap().keySet());

      keys.remove("sign");//排除sign參數(shù)

      Collections.sort(keys);//排序

      StringBuilder sb = new StringBuilder();

      for (String key : keys) {

      sb.append(key).append("=").append(request.getParameter(key)).append("&");//拼接字符串

      }

      String linkString = sb.toString();

      linkString = StringUtils.substring(linkString, 0, linkString.length() - 1);//去除最后一個'&'

      String secret = "Potato";//密鑰,自己修改

      String sign = DigestUtils.md5Hex(linkString + secret);//混合密鑰md5

      return StringUtils.equals(sign, requestSign);//比較

      }

      集成MyBatis、通用Mapper插件、PageHelper分頁插件,實現(xiàn)單表業(yè)務(wù)零SQL

      使用Druid Spring Boot Starter 集成Druid數(shù)據(jù)庫連接池與監(jiān)控

      使用FastJsonHttpMessageConverter,提高JSON序列化速度

      技術(shù)選型&文檔

      Spring Boot:https://www.jianshu.com/p/1a9fd8936bd8MyBatis:http://www.mybatis.org/mybatis-3/zh/index.htmlMyBatisb通用Mapper插件:https://mapperhelper.github.io/docs/MyBatis PageHelper分頁插件:https://pagehelper.github.io/Druid Spring Boot Starter:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter/Fastjson:https://github.com/Alibaba/fastjson/wiki/%E9%A6%96%E9%A1%B5

      API Spring Spring Boot

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:如何選擇雙數(shù)行(單數(shù)行和雙數(shù)行分開)
      下一篇:怎么根據(jù)背景顏色統(tǒng)計個數(shù)(如何用顏色統(tǒng)計個數(shù))
      相關(guān)文章
      亚洲精品免费在线视频| 亚洲乱码中文字幕综合| 亚洲美女aⅴ久久久91| 久久久久亚洲精品男人的天堂| 国产亚洲综合久久| 亚洲精品永久在线观看| 亚洲日本人成中文字幕| 亚洲综合综合在线| 亚洲国产成人久久精品动漫| 午夜亚洲AV日韩AV无码大全| 亚洲国产精品线在线观看| 亚洲高清视频在线观看| 亚洲AV无码成人专区片在线观看 | 亚洲2022国产成人精品无码区| 亚洲乱亚洲乱妇无码麻豆| 亚洲国产精品VA在线看黑人| 亚洲va无码va在线va天堂| 久久亚洲一区二区| 亚洲AV无码1区2区久久| 亚洲国产精品久久久久婷婷软件 | 亚洲爆乳AAA无码专区| 色偷偷亚洲第一综合| 无码一区二区三区亚洲人妻| 亚洲成a人片在线观看老师| 狠狠综合久久综合88亚洲| 亚洲精品字幕在线观看| 亚洲天天做日日做天天看| 久久久久亚洲AV成人无码| 亚洲欧洲国产经精品香蕉网| 亚洲日本人成中文字幕| 亚洲精品9999久久久久无码| 色窝窝亚洲av网| 国产亚洲情侣一区二区无码AV| 亚洲国产精品成人精品无码区| 久久精品亚洲一区二区三区浴池 | 精品国产日韩亚洲一区| 国产精品国产亚洲精品看不卡| 精品亚洲A∨无码一区二区三区| 亚洲天堂一区二区三区| 亚洲精品乱码久久久久久蜜桃图片 | 亚洲aⅴ无码专区在线观看春色|