点击上方 "程序员小乐"关注, 星标或置顶一起成长
每天凌晨00点00分, 第一时间与你相约
每日英文
You can't just sit there and wait for life to come to you. You have to go get it.
你不能无所事事的坐等人生带给你一切,你必须得自己努力争取。
每日掏心话
生活总会给你另一个机会,这个机会叫明天。生活没有过去,也没有曾经,不管什么事只要过去了,就会慢慢忘掉。
作者:简单的土豆 | 责编:乐乐
来自:ImportNew
往日回顾:手把手教你如何搭建一款自己的私有百度网盘?
正文
最近使用Spring Boot 配合 MyBatis 、通用Mapper插件、PageHelper分页插件 连做了几个中小型API项目,做下来觉得这套框架、工具搭配起来开发这种项目确实非常舒服,团队的反响也不错。在项目搭建和开发的过程中也总结了一些小经验,与大家分享一下。
在开发一个API项目之前,搭建项目、引入依赖、配置框架这些基础活自然不用多说,通常为了加快项目的开发进度(早点回家)还需要封装一些常用的类和工具,比如统一的响应结果封装、统一的异常处理、接口签名认证、基础的增删改差方法封装、基础代码生成工具等等,有了这些项目才能开工。
然而,下次再做类似的项目上述那些步骤可能还要搞一遍,虽然通常是拿过来改改,但是还是比较浪费时间。所以,可以利用面向对象抽象、封装的思想,抽取这类项目的共同之处封装成了一个种子项目(估计大部分公司都会有很多类似的种子项目),这样的话下次再开发类似的项目直接在该种子项目上迭代就可以了,减少无意义的重复工作。
在相关项目上线之后,我花了点时间对该种子项目做了一些精简,并且已经把该项目分享到GitHub上面了,如果你正准备做类似项目的话,可以去克隆下来试试。
项目地址&使用文档:https://github.com/lihengming/spring-boot-api-project-seed 。
注:使用代码生成器生成代码后会创建model、dao、service、web等包。
统一响应结果封装及生成工具
/** * 统一API响应结果封装 */publicclass Result { privateint code; private String message; private Object data; public Result setCode(ResultCode resultCode) { this.code = resultCode.code; returnthis; } //省略getter、setter方法}
/** * 响应码枚举,参考HTTP状态码的语义 */publicenum ResultCode { SUCCESS(200),//成功 FAIL(400),//失败 UNAUTHORIZED(401),//未认证(签名错误) NOT_FOUND(404),//接口不存在 INTERNAL_SERVER_ERROR(500);//服务器内部错误 publicint code; ResultCode(int code) { this.code = code; }}/** * 响应结果生成工具 */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); }}
统一异常处理
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()); } 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() + "] 内部错误,请联系管理员"); 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); returnnew ModelAndView(); } }); }
publicinterface 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();//获取所有}
publicabstractclass CodeGenerator { ... public static void main(String[] args) { genCode("输入表名"); } public static void genCode(String... tableNames) { for (String tableName : tableNames) { //根据需求生成,不需要的注掉,模板有问题的话可以自己修改。 genModelAndMapper(tableName); genService(tableName); genController(tableName); } } ...}
public void addInterceptors(InterceptorRegistry registry) { //接口签名认证拦截器,该签名认证比较简单,实际项目中可以使用Json Web Token或其他更好的方式替代。 if (!"dev".equals(env)) { //开发环境忽略签名认证 registry.addInterceptor(new HandlerInterceptorAdapter() { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //验证签名 boolean pass = validateSign(request); if (pass) { returntrue; } else { logger.warn("签名认证失败,请求接口:{},请求IP:{},请求参数:{}", request.getRequestURI(), getIpAddress(request), JSON.toJSONString(request.getParameterMap())); Result result = new Result(); result.setCode(ResultCode.UNAUTHORIZED).setMessage("签名认证失败"); responseResult(response, result); returnfalse; } } }); }}
/** * 一个简单的签名认证,规则: * 1. 将请求参数按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)) { returnfalse; } List<String> keys = new ArrayList<String>(request.getParameterMap().keySet()); keys.remove("sign");//排除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);//混合密钥md5 return StringUtils.equals(sign, requestSign);//比较}
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
欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。
欢迎各位读者加入订阅号程序员小乐技术群,在后台回复“加群”或者“学习”即可。
猜你还想看
阿里、腾讯、百度、华为、京东最新面试题汇集
5万字长文!SpringBoot 操作 ElasticSearch 详解
关于 MyBatis 我总结了 10 种通用的写法
分布式事务之 RocketMQ 事务消息详解
文章有问题?点此查看未经处理的缓存