分享一个基于Spring Boot的API、RESTful API项目种子(骨架)!


前言特征&提供技术选型&文档

前言

最近使用Spring Boot 配合 MyBatis 、通用Mapper插件、PageHelper分页插件 连做了几个中小型API项目,做下来觉得这套框架、工具搭配起来开发这种项目确实非常舒服,团队的反响也不错。在项目搭建和开发的过程中也总结了一些小经验,与大家分享一下。

在开发一个API项目之前,搭建项目、引入依赖、配置框架这些基础活自然不用多说,通常为了加快项目的开发进度(早点回家)还需要封装一些常用的类和工具,比如统一的响应结果封装、统一的异常处理、接口签名认证、基础的增删改差方法封装、基础代码生成工具等等,有了这些项目才能开工。

然而,下次再做类似的项目上述那些步骤可能还要搞一遍,虽然通常是拿过来改改,但是还是比较浪费时间。所以,可以利用面向对象抽象、封装的思想,抽取这类项目的共同之处封装成了一个种子项目(估计大部分公司都会有很多类似的种子项目),这样的话下次再开发类似的项目直接在该种子项目上迭代就可以了,减少无意义的重复工作。

在相关项目上线之后,我花了点时间对该种子项目做了一些精简,并且已经把该项目分享到GitHub上面了,如果你正准备做类似项目的话,可以去克隆下来试试。

如果在使用中发现问题或者有什么好建议的话欢迎提issue或pr一起来完善它。

特征&提供

最佳实践的项目结构、配置文件、精简的POM

注:使用代码生成器生成代码后会创建model、dao、service、web等包。

统一响应结果封装及生成工具

<code>

public

class

Result

{

private

int code;

private

String message;

private

Object

data

;

public

Result setCode(ResultCode resultCode) {

this

.code = resultCode.code;

return

this

; } }/<code>

<code>

public

enum

ResultCode { SUCCESS(

200

), FAIL(

400

), UNAUTHORIZED(

401

), NOT_FOUND(

404

), INTERNAL_SERVER_ERROR(

500

);

public

int

code; ResultCode(

int

code) {

this

.code = code; } }/<code>

<code>

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); } }/<code>

统一异常处理

<code>

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) { 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() +

"] 内部错误,请联系管理员"

);

String

message;

if

(handler

instanceof

HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; message =

String

.format(

"接口 [%s] 出现异常,方法:%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(); } }); }/<code>

常用基础方法抽象封装

<code>

public

interface

Service

<

T

>

{

void

save

(T model)

;

void

save

(List models)

;

void

deleteById

(Integer id)

;

void

deleteByIds

(String ids)

;

void

update

(T model)

;

T

findById

(Integer id)

;

T

findBy

(String fieldName, Object value)

throws

TooManyResultsException

;

List

findByIds

(String ids)

;

List

findByCondition

(Condition condition)

;

List

findAll

()

; }/<code>

提供代码生成器来生成基础代码

<code>

public

abstract

class

CodeGenerator

{ ...

public

static

void

main

(

String[] args

)

{ genCode(

"输入表名"

); }

public

static

void

genCode

(

String... tableNames

)

{

for

(String tableName : tableNames) { genModelAndMapper(tableName); genService(tableName); genController(tableName); } } ... }/<code>

CodeGenerator 可根据表名生成对应的Model、Mapper、MapperXML、Service、ServiceImpl、Controller(默认提供POST和RESTful两套Controller模板,根据需要在 genController(tableName)方法中自己选择,默认是纯POST的),代码模板可根据实际项目的需求来定制,以便渐少重复劳动。

由于每个公司业务都不太一样,所以只提供了一些简单的通用方法模板,主要是提供一个思路来减少重复代码的编写。在我们公司的实际使用中,其实根据业务的抽象编写了大量的代码模板。

提供简单的接口签名认证

<code>

public

void

addInterceptors

(InterceptorRegistry registry)

{

if

(!

"dev"

.equals(env)) { registry.addInterceptor(

new

HandlerInterceptorAdapter() {

public

boolean

preHandle

(HttpServletRequest request, HttpServletResponse response, Object handler)

throws

Exception

{

boolean

pass = validateSign(request);

if

(pass) {

return

true

; }

else

{ logger.warn(

"签名认证失败,请求接口:{},请求IP:{},请求参数:{}"

, request.getRequestURI(), getIpAddress(request), JSON.toJSONString(request.getParameterMap())); Result result =

new

Result(); result.setCode(ResultCode.UNAUTHORIZED).setMessage(

"签名认证失败"

); responseResult(response, result);

return

false

; } } }); } }/<code>

<code>

private

boolean

validateSign(HttpServletRequest request) {

String

requestSign = request.getParameter(

"sign"

);

if

(StringUtils.isEmpty(requestSign)) {

return

false

; } List<

String

> keys =

new

ArrayList<

String

>(request.getParameterMap().keySet()); keys.remove(

"sign"

); 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);

return

StringUtils.equals(sign, requestSign); }/<code>

集成MyBatis、通用Mapper插件、PageHelper分页插件,实现单表业务零SQL

使用Druid Spring Boot Starter 集成Druid数据库连接池与监控

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

技术选型&文档

Spring Boot:https://www.jianshu.com/p/1a9fd8936bd8

MyBatis:http://www.mybatis.org/mybatis-3/zh/index.html

MyBatisb通用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

后言

感谢大家的支持,没想到一个简单的项目总结分享在短短两天获得这么多人的关注,还上了GitHub Trending榜单,有点受宠若惊哈,话说现在国内技术社区氛围真是越来越好了,希望大家有时间的话都能参与到开源分享的行列来,分享知识,快乐编码,共勉。