• 请不要在回答技术问题时复制粘贴 AI 生成的内容
lihengming
V2EX  ›  程序员

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

  •  
  •   lihengming · Jul 4, 2017 · 3179 views
  •   You need to sign in to view this topic
    This topic created in 3402 days ago, the information mentioned may be changed or developed.

    前言

    最近使用 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 序列化速度

    技术选型&文档

    No Comments Yet
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   5114 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 45ms · UTC 06:47 · PVG 14:47 · LAX 23:47 · JFK 02:47
    ♥ Do have faith in what you're doing.