V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
lihengming
V2EX  ›  程序员

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

  •  
  •   lihengming · 2017-07-04 16:51:57 +08:00 · 2480 次点击
    这是一个创建于 2517 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

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

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

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

    在相关项目上线之后,我花了点时间对该种子项目做了一些精简,并且已经把该项目分享到 GitHub 上面了,如果你正准备做类似项目的话,可以去克隆下来试试,项目地址&使用文档: https://github.com/lihengming/spring-boot-api-project-seed 。如果在使用中发现问题或者有什么好建议的话欢迎提 issue 或 pr 一起来完善它。

    特征&提供

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

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

    • 统一响应结果封装及生成工具
    /**
     * 统一 API 响应结果封装
     */
    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 方法
    }
    
    /**
     * 响应码枚举,参考 HTTP 状态码的语义
     */
    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;
        }
    }
    
    /**
     * 响应结果生成工具
     */
    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);
        }
    }
    
    • 统一异常处理
      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) {//业务失败的异常,如“账号或密码错误”
                        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();
                }
    
            });
        }
    
    • 常用基础方法抽象封装
    public interface Service<T> {
        void save(T model);//持久化
        void save(List<T> 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 中某个成员变量名称(非数据表中 column 的名称)查找,value 需符合 unique 约束
        List<T> findByIds(String ids);//通过多个 ID 查找 //eg:ids -> “ 1,2,3,4 ”
        List<T> findByCondition(Condition condition);//根据条件查找
        List<T> findAll();//获取所有
    }
    
    • 提供代码生成器来生成基础代码
    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);
            }
        }
      ...
    }
    

    CodeGenerator 可根据表名生成对应的 Model、Mapper、MapperXML、Service、ServiceImpl、Controller (默认提供 POST 和 RESTful 两套 Controller 模板,根据需要在 genController(tableName)方法中自己选择,默认是纯 POST 的),代码模板可根据实际项目的需求来定制,以便渐少重复劳动。由于每个公司业务都不太一样,所以只提供了一些简单的通用方法模板,主要是提供一个思路来减少重复代码的编写。在我们公司的实际使用中,其实根据业务的抽象编写了大量的代码模板。

    • 提供简单的接口签名认证

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

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

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

    技术选型&文档

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   905 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 21:02 · PVG 05:02 · LAX 14:02 · JFK 17:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.