javascript
一个基于 Spring Boot 的项目骨架,拿走即用
??點(diǎn)擊上方?好好學(xué)java?,選擇?星標(biāo)?公眾號(hào)
重磅資訊、干貨,第一時(shí)間送達(dá) 今日推薦:程序員入職國(guó)企,1周上班5小時(shí),曬出薪資感嘆:騰訊當(dāng)CEO也不去個(gè)人原創(chuàng)+1博客:點(diǎn)擊前往,查看更多作者:簡(jiǎn)單的土豆
最近使用Spring Boot 配合 MyBatis 、通用Mapper插件、PageHelper分頁(yè)插件 連做了幾個(gè)中小型API項(xiàng)目,做下來(lái)覺(jué)得這套框架、工具搭配起來(lái)開(kāi)發(fā)這種項(xiàng)目確實(shí)非常舒服,團(tuán)隊(duì)的反響也不錯(cuò)。在項(xiàng)目搭建和開(kāi)發(fā)的過(guò)程中也總結(jié)了一些小經(jīng)驗(yàn),與大家分享一下。
在開(kāi)發(fā)一個(gè)API項(xiàng)目之前,搭建項(xiàng)目、引入依賴(lài)、配置框架這些基礎(chǔ)活自然不用多說(shuō),通常為了加快項(xiàng)目的開(kāi)發(fā)進(jìn)度(早點(diǎn)回家)還需要封裝一些常用的類(lèi)和工具,比如統(tǒng)一的響應(yīng)結(jié)果封裝、統(tǒng)一的異常處理、接口簽名認(rèn)證、基礎(chǔ)的增刪改差方法封裝、基礎(chǔ)代碼生成工具等等,有了這些項(xiàng)目才能開(kāi)工。
然而,下次再做類(lèi)似的項(xiàng)目上述那些步驟可能還要搞一遍,雖然通常是拿過(guò)來(lái)改改,但是還是比較浪費(fèi)時(shí)間。所以,可以利用面向?qū)ο蟪橄蟆⒎庋b的思想,抽取這類(lèi)項(xiàng)目的共同之處封裝成了一個(gè)種子項(xiàng)目(估計(jì)大部分公司都會(huì)有很多類(lèi)似的種子項(xiàng)目),這樣的話下次再開(kāi)發(fā)類(lèi)似的項(xiàng)目直接在該種子項(xiàng)目上迭代就可以了,減少無(wú)意義的重復(fù)工作。
在相關(guān)項(xiàng)目上線之后,我花了點(diǎn)時(shí)間對(duì)該種子項(xiàng)目做了一些精簡(jiǎn),并且已經(jīng)把該項(xiàng)目分享到GitHub上面了,如果你正準(zhǔn)備做類(lèi)似項(xiàng)目的話,可以去克隆下來(lái)試試。
附上我歷時(shí)三個(gè)月總結(jié)的?Java 面試 + Java 后端技術(shù)學(xué)習(xí)指南,這是本人這幾年及春招的總結(jié),目前,已經(jīng)拿到了大廠offer,拿去不謝!
下載方式
1.?首先掃描下方二維碼
2.?后臺(tái)回復(fù)「Java面試」即可獲取
項(xiàng)目地址&使用文檔:https://github.com/lihengming/spring-boot-api-project-seed 。如果在使用中發(fā)現(xiàn)問(wèn)題或者有什么好建議的話歡迎提issue或pr一起來(lái)完善它。
特征&提供
最佳實(shí)踐的項(xiàng)目結(jié)構(gòu)、配置文件、精簡(jiǎn)的POM
注:使用代碼生成器生成代碼后會(huì)創(chuàng)建model、dao、service、web等包。
統(tǒng)一響應(yīng)結(jié)果封裝及生成工具
/***?統(tǒng)一API響應(yīng)結(jié)果封裝*/ publicclass?Result?{privateint?code;private?String?message;private?Object?data;public?Result?setCode(ResultCode?resultCode)?{this.code?=?resultCode.code;returnthis;}//省略getter、setter方法 } /***?響應(yīng)碼枚舉,參考HTTP狀態(tài)碼的語(yǔ)義*/ publicenum?ResultCode?{SUCCESS(200),//成功FAIL(400),//失敗UNAUTHORIZED(401),//未認(rèn)證(簽名錯(cuò)誤)NOT_FOUND(404),//接口不存在INTERNAL_SERVER_ERROR(500);//服務(wù)器內(nèi)部錯(cuò)誤publicint?code;ResultCode(int?code)?{this.code?=?code;} } /***?響應(yīng)結(jié)果生成工具*/ publicclass?ResultGenerator?{privatestaticfinal?String?DEFAULT_SUCCESS_MESSAGE?=?"SUCCESS";public?static?Result?genSuccessResult()?{returnnew?Result().setCode(ResultCode.SUCCESS).setMessage(DEFAULT_SUCCESS_MESSAGE);}public?static?Result?genSuccessResult(Object?data)?{returnnew?Result().setCode(ResultCode.SUCCESS).setMessage(DEFAULT_SUCCESS_MESSAGE).setData(data);}public?static?Result?genFailResult(String?message)?{returnnew?Result().setCode(ResultCode.FAIL).setMessage(message);} }統(tǒng)一異常處理
public?void?configureHandlerExceptionResolvers(List<HandlerExceptionResolver>?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ù)失敗的異常,如“賬號(hào)或密碼錯(cuò)誤”result.setCode(ResultCode.FAIL).setMessage(e.getMessage());logger.info(e.getMessage());}?elseif?(e?instanceof?NoHandlerFoundException)?{result.setCode(ResultCode.NOT_FOUND).setMessage("接口?["?+?request.getRequestURI()?+?"]?不存在");}?elseif?(e?instanceof?ServletException)?{result.setCode(ResultCode.FAIL).setMessage(e.getMessage());}?else?{result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口?["?+?request.getRequestURI()?+?"]?內(nèi)部錯(cuò)誤,請(qǐng)聯(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);returnnew?ModelAndView();}});}常用基礎(chǔ)方法抽象封裝
publicinterface?Service<T>?{void?save(T?model);//持久化void?save(List<T>?models);//批量持久化void?deleteById(Integer?id);//通過(guò)主鍵刪除void?deleteByIds(String?ids);//批量刪除 eg:ids ->?“1,2,3,4”void?update(T?model);//更新T?findById(Integer?id);//通過(guò)ID查找T?findBy(String?fieldName,?Object?value)?throws?TooManyResultsException;?//通過(guò)Model中某個(gè)成員變量名稱(chēng)(非數(shù)據(jù)表中column的名稱(chēng))查找,value需符合unique約束List<T>?findByIds(String?ids);//通過(guò)多個(gè)ID查找//eg:ids ->?“1,2,3,4”List<T>?findByCondition(Condition?condition);//根據(jù)條件查找List<T>?findAll();//獲取所有 }提供代碼生成器來(lái)生成基礎(chǔ)代碼
publicabstractclass?CodeGenerator?{...public?static?void?main(String[]?args)?{genCode("輸入表名");}public?static?void?genCode(String...?tableNames)?{for?(String?tableName?:?tableNames)?{//根據(jù)需求生成,不需要的注掉,模板有問(wèn)題的話可以自己修改。genModelAndMapper(tableName);genService(tableName);genController(tableName);}}... }CodeGenerator
可根據(jù)表名生成對(duì)應(yīng)的Model、Mapper、MapperXML、Service、ServiceImpl、Controller(默認(rèn)提供POST和RESTful兩套Controller模板,根據(jù)需要在 genController(tableName)方法中自己選擇,默認(rèn)是純POST的),代碼模板可根據(jù)實(shí)際項(xiàng)目的需求來(lái)定制,以便漸少重復(fù)勞動(dòng)。由于每個(gè)公司業(yè)務(wù)都不太一樣,所以只提供了一些簡(jiǎn)單的通用方法模板,主要是提供一個(gè)思路來(lái)減少重復(fù)代碼的編寫(xiě)。在我們公司的實(shí)際使用中,其實(shí)根據(jù)業(yè)務(wù)的抽象編寫(xiě)了大量的代碼模板。提供簡(jiǎn)單的接口簽名認(rèn)證
public?void?addInterceptors(InterceptorRegistry?registry)?{//接口簽名認(rèn)證攔截器,該簽名認(rèn)證比較簡(jiǎn)單,實(shí)際項(xiàng)目中可以使用Json Web Token或其他更好的方式替代。if?(!"dev".equals(env))?{?//開(kāi)發(fā)環(huán)境忽略簽名認(rèn)證registry.addInterceptor(new?HandlerInterceptorAdapter()?{@Overridepublic?boolean?preHandle(HttpServletRequest?request,?HttpServletResponse?response,?Object?handler)?throws?Exception?{//驗(yàn)證簽名boolean?pass?=?validateSign(request);if?(pass)?{returntrue;}?else?{logger.warn("簽名認(rèn)證失敗,請(qǐng)求接口:{},請(qǐng)求IP:{},請(qǐng)求參數(shù):{}",request.getRequestURI(),?getIpAddress(request),?JSON.toJSONString(request.getParameterMap()));Result?result?=?new?Result();result.setCode(ResultCode.UNAUTHORIZED).setMessage("簽名認(rèn)證失敗");responseResult(response,?result);returnfalse;}}});} } /***?一個(gè)簡(jiǎn)單的簽名認(rèn)證,規(guī)則:*?1.?將請(qǐng)求參數(shù)按ascii碼排序*?2.?拼接為a=value&b=value...這樣的字符串(不包含sign)*?3.?混合密鑰(secret)進(jìn)行md5獲得簽名,與請(qǐng)求的簽名進(jìn)行比較*/ private?boolean?validateSign(HttpServletRequest?request)?{String?requestSign?=?request.getParameter("sign");//獲得請(qǐng)求簽名,如sign=19e907700db7ad91318424a97c54ed57if?(StringUtils.isEmpty(requestSign))?{returnfalse;}List<String>?keys?=?new?ArrayList<String>(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);//去除最后一個(gè)'&'String?secret?=?"Potato";//密鑰,自己修改String?sign?=?DigestUtils.md5Hex(linkString?+?secret);//混合密鑰md5return?StringUtils.equals(sign,?requestSign);//比較 }集成MyBatis、通用Mapper插件、PageHelper分頁(yè)插件,實(shí)現(xiàn)單表業(yè)務(wù)零SQL使用Druid Spring Boot Starter 集成Druid數(shù)據(jù)庫(kù)連接池與監(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分頁(yè)插件: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最后,再附上我歷時(shí)三個(gè)月總結(jié)的?Java 面試 + Java 后端技術(shù)學(xué)習(xí)指南,這是本人這幾年及春招的總結(jié),目前,已經(jīng)拿到了大廠offer,拿去不謝!
下載方式
1.?首先掃描下方二維碼
2.?后臺(tái)回復(fù)「Java面試」即可獲取
總結(jié)
以上是生活随笔為你收集整理的一个基于 Spring Boot 的项目骨架,拿走即用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 程序员入职国企,1周上班5小时,晒出薪资
- 下一篇: 面试官:Spring事务的传播行为有几种