开源项目

开源项目

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

Spring Boot简单的土豆 发表了文章 • 32 个评论 • 2028 次浏览 • 2017-07-04 17:48 • 来自相关话题

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

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

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

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

                           (新标签打开图片,查看大图)
注:使用代码生成器生成代码后会创建model、dao、service、web等包。

- 统一响应结果封装及生成工具$(document).ready(function() {$('pre code').each(function(i, block) { hljs.highlightBlock( block); }); });/**
* 统一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的),代码模板可根据实际项目的需求来定制,以便渐少重复劳动。由于每个公司业务都不太一样,所以只提供了一些简单的通用方法模板,主要是提供一个思路来减少重复代码的编写。在我们公司的实际使用中,其实根据业务的抽象编写了大量的代码模板。


- 提供简单的接口签名认证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) {
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;
}
}
});
}
}/**
* 一个简单的签名认证,规则:
* 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)) {
return false;
}
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);//比较
}
- 集成MyBatis、通用Mapper插件、PageHelper分页插件,实现单表业务零SQL

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

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

技术选型&文档
- Spring Boot(查看Spring Boot学习&使用指南)
- MyBatis(查看官方中文文档)
- MyBatisb通用Mapper插件(查看官方中文文档)
- MyBatis PageHelper分页插件(查看官方中文文档)
- Druid Spring Boot Starter(查看官方中文文档)
- Fastjson(查看官方中文文档)

注:代码贴上去有点乱,可以查看我的简书原文 查看全部

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

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

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

在相关项目上线之后,我花了点时间对该种子项目做了一些精简,并且已经把该项目分享到GitHub上面了,如果你正准备做类似项目的话,可以去克隆下来试试,项目地址&使用文档:https://github.com/lihengming/ ... -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的),代码模板可根据实际项目的需求来定制,以便渐少重复劳动。由于每个公司业务都不太一样,所以只提供了一些简单的通用方法模板,主要是提供一个思路来减少重复代码的编写。在我们公司的实际使用中,其实根据业务的抽象编写了大量的代码模板。


- 提供简单的接口签名认证
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) {
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;
}
}
});
}
}
/**
* 一个简单的签名认证,规则:
* 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)) {
return false;
}
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);//比较
}

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

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

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

技术选型&文档
- Spring Boot(查看Spring Boot学习&使用指南
- MyBatis(查看官方中文文档
- MyBatisb通用Mapper插件(查看官方中文文档
- MyBatis PageHelper分页插件(查看官方中文文档
- Druid Spring Boot Starter(查看官方中文文档
- Fastjson(查看官方中文文档

注:代码贴上去有点乱,可以查看我的简书原文

开源spring-boot-starter-dubbox

Spring BootJThink 发表了文章 • 0 个评论 • 214 次浏览 • 2017-06-26 19:31 • 来自相关话题

自定义的spring-boot的dubbox starter,为spring-boot相关的项目使用dubbox提供简易的方式并集成spring-boot的auto configuration。见github:https://github.com/JThink/spring-boot-starter-dubbox





  查看全部
自定义的spring-boot的dubbox starter,为spring-boot相关的项目使用dubbox提供简易的方式并集成spring-boot的auto configuration。见github:https://github.com/JThink/spring-boot-starter-dubbox

项目介绍.png

 

开源spring-boot-starter-hbase

开源项目JThink 发表了文章 • 12 个评论 • 1024 次浏览 • 2017-06-13 18:08 • 来自相关话题

自定义的spring-boot的hbase starter,为hbase的query和更新等操作提供简易的api并集成spring-boot的auto configuration见github:https://github.com/JThink/spri ... hbase 查看全部
自定义的spring-boot的hbase starter,为hbase的query和更新等操作提供简易的api并集成spring-boot的auto configuration见github:https://github.com/JThink/spri ... hbase

开源分布式日志采集系统

开源项目JThink 发表了文章 • 0 个评论 • 1845 次浏览 • 2017-06-09 15:43 • 来自相关话题

分享一个开源的分布式日志采集系统,该系统对java、scala等运行于jvm的程序进行实时日志采集、索引和可视化,对系统进行进程级别的监控,对系统内部的操作进行策略性的报警、对分布式的rpc调用进行trace跟踪以便于进行性能分析。
github地址:https://github.com/JThink/SkyEye
部分资料:http://blog.csdn.net/column/details/14346.html
可以线下多交流交流
交流群:624054633
我的Email:leviqian@sina.com 查看全部
分享一个开源的分布式日志采集系统,该系统对java、scala等运行于jvm的程序进行实时日志采集、索引和可视化,对系统进行进程级别的监控,对系统内部的操作进行策略性的报警、对分布式的rpc调用进行trace跟踪以便于进行性能分析。
github地址:https://github.com/JThink/SkyEye
部分资料:http://blog.csdn.net/column/details/14346.html
可以线下多交流交流
交流群:624054633
我的Email:leviqian@sina.com

打算最近把一个基于redis的缓存组件开源了,大家期待着吧

开源项目appletang 回复了问题 • 18 人关注 • 13 个回复 • 1316 次浏览 • 2017-06-08 23:54 • 来自相关话题

springboot-learning-example

开源项目泥瓦匠BYSocket 发表了文章 • 0 个评论 • 864 次浏览 • 2017-06-06 17:29 • 来自相关话题

项目名称:springboot-learning-example
项目地址:https://github.com/JeffLi1993/ ... ample

项目介绍:spring boot 实践学习案例,是 spring boot 初学者及核心技术巩固的最佳实践。
『 基础 - 入门篇 』
springboot-helloworld - 《Spring Boot 之 HelloWorld 详解》
springboot-properties - 《Spring Boot 之配置文件详解》

『 基础 - Web 业务开发篇 』
springboot-restful -《Springboot 实现 Restful 服务,基于 HTTP / JSON 传输》
springboot-freemarker -《Spring Boot 集成 FreeMarker 详解案例》
springboot-validation-over-json -《Spring Boot HTTP over JSON 的错误码异常处理》

『 基础 – 数据存储篇 』
springboot-mybatis -《Springboot 整合 Mybatis 的完整 Web 案例》
springboot-mybatis-annotation -《Spring Boot 整合 Mybatis Annotation 注解的完整 Web 案例》
springboot-mybatis-mutil-datasource -《Spring Boot 整合 Mybatis 实现 Druid 多数据源详解》

『 基础 – 数据缓存篇 』
springboot-mybatis-redis -《Spring Boot 整合 Redis 实现缓存操作》
springboot-mybatis-redis-annotation -《Spring Boot 注解实现整合 Redis 作为缓存》

『 其他篇 』
springboot-elasticsearch -《Spring Boot 整合 Elasticsearch,实现 function score query 权重分查询》
springboot-dubbo-server 
springboot-dubbo-client - 《Springboot 整合 Dubbo/ZooKeeper 详解 SOA 案例》



  查看全部
项目名称:springboot-learning-example
项目地址:https://github.com/JeffLi1993/ ... ample

项目介绍:spring boot 实践学习案例,是 spring boot 初学者及核心技术巩固的最佳实践。
『 基础 - 入门篇 』
springboot-helloworld - 《Spring Boot 之 HelloWorld 详解》
springboot-properties - 《Spring Boot 之配置文件详解》

『 基础 - Web 业务开发篇 』
springboot-restful -《Springboot 实现 Restful 服务,基于 HTTP / JSON 传输》
springboot-freemarker -《Spring Boot 集成 FreeMarker 详解案例》
springboot-validation-over-json -《Spring Boot HTTP over JSON 的错误码异常处理》

『 基础 – 数据存储篇 』
springboot-mybatis -《Springboot 整合 Mybatis 的完整 Web 案例》
springboot-mybatis-annotation -《Spring Boot 整合 Mybatis Annotation 注解的完整 Web 案例》
springboot-mybatis-mutil-datasource -《Spring Boot 整合 Mybatis 实现 Druid 多数据源详解》

『 基础 – 数据缓存篇 』
springboot-mybatis-redis -《Spring Boot 整合 Redis 实现缓存操作》
springboot-mybatis-redis-annotation -《Spring Boot 注解实现整合 Redis 作为缓存》

『 其他篇 』
springboot-elasticsearch -《Spring Boot 整合 Elasticsearch,实现 function score query 权重分查询》
springboot-dubbo-server 
springboot-dubbo-client - 《Springboot 整合 Dubbo/ZooKeeper 详解 SOA 案例》



 

打算最近把一个基于redis的缓存组件开源了,大家期待着吧

回复

开源项目appletang 回复了问题 • 18 人关注 • 13 个回复 • 1316 次浏览 • 2017-06-08 23:54 • 来自相关话题

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

Spring Boot简单的土豆 发表了文章 • 32 个评论 • 2028 次浏览 • 2017-07-04 17:48 • 来自相关话题

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

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

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

在相关项目上线之后,我花了点时间对该种子项目做了一些精简,并且已经把该项目分享到GitHub上面了,如果你正准备做类似项目的话,可以去克隆下来试试,项目地址&使用文档:https://github.com/lihengming/ ... -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的),代码模板可根据实际项目的需求来定制,以便渐少重复劳动。由于每个公司业务都不太一样,所以只提供了一些简单的通用方法模板,主要是提供一个思路来减少重复代码的编写。在我们公司的实际使用中,其实根据业务的抽象编写了大量的代码模板。


- 提供简单的接口签名认证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) {
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;
}
}
});
}
}/**
* 一个简单的签名认证,规则:
* 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)) {
return false;
}
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);//比较
}
- 集成MyBatis、通用Mapper插件、PageHelper分页插件,实现单表业务零SQL

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

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

技术选型&文档
- Spring Boot(查看Spring Boot学习&使用指南)
- MyBatis(查看官方中文文档)
- MyBatisb通用Mapper插件(查看官方中文文档)
- MyBatis PageHelper分页插件(查看官方中文文档)
- Druid Spring Boot Starter(查看官方中文文档)
- Fastjson(查看官方中文文档)

注:代码贴上去有点乱,可以查看我的简书原文 查看全部

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

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

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

在相关项目上线之后,我花了点时间对该种子项目做了一些精简,并且已经把该项目分享到GitHub上面了,如果你正准备做类似项目的话,可以去克隆下来试试,项目地址&使用文档:https://github.com/lihengming/ ... -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的),代码模板可根据实际项目的需求来定制,以便渐少重复劳动。由于每个公司业务都不太一样,所以只提供了一些简单的通用方法模板,主要是提供一个思路来减少重复代码的编写。在我们公司的实际使用中,其实根据业务的抽象编写了大量的代码模板。


- 提供简单的接口签名认证
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) {
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;
}
}
});
}
}
/**
* 一个简单的签名认证,规则:
* 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)) {
return false;
}
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);//比较
}

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

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

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

技术选型&文档
- Spring Boot(查看Spring Boot学习&使用指南
- MyBatis(查看官方中文文档
- MyBatisb通用Mapper插件(查看官方中文文档
- MyBatis PageHelper分页插件(查看官方中文文档
- Druid Spring Boot Starter(查看官方中文文档
- Fastjson(查看官方中文文档

注:代码贴上去有点乱,可以查看我的简书原文

开源spring-boot-starter-dubbox

Spring BootJThink 发表了文章 • 0 个评论 • 214 次浏览 • 2017-06-26 19:31 • 来自相关话题

自定义的spring-boot的dubbox starter,为spring-boot相关的项目使用dubbox提供简易的方式并集成spring-boot的auto configuration。见github:https://github.com/JThink/spring-boot-starter-dubbox





  查看全部
自定义的spring-boot的dubbox starter,为spring-boot相关的项目使用dubbox提供简易的方式并集成spring-boot的auto configuration。见github:https://github.com/JThink/spring-boot-starter-dubbox

项目介绍.png

 

开源spring-boot-starter-hbase

开源项目JThink 发表了文章 • 12 个评论 • 1024 次浏览 • 2017-06-13 18:08 • 来自相关话题

自定义的spring-boot的hbase starter,为hbase的query和更新等操作提供简易的api并集成spring-boot的auto configuration见github:https://github.com/JThink/spri ... hbase 查看全部
自定义的spring-boot的hbase starter,为hbase的query和更新等操作提供简易的api并集成spring-boot的auto configuration见github:https://github.com/JThink/spri ... hbase

开源分布式日志采集系统

开源项目JThink 发表了文章 • 0 个评论 • 1845 次浏览 • 2017-06-09 15:43 • 来自相关话题

分享一个开源的分布式日志采集系统,该系统对java、scala等运行于jvm的程序进行实时日志采集、索引和可视化,对系统进行进程级别的监控,对系统内部的操作进行策略性的报警、对分布式的rpc调用进行trace跟踪以便于进行性能分析。
github地址:https://github.com/JThink/SkyEye
部分资料:http://blog.csdn.net/column/details/14346.html
可以线下多交流交流
交流群:624054633
我的Email:leviqian@sina.com 查看全部
分享一个开源的分布式日志采集系统,该系统对java、scala等运行于jvm的程序进行实时日志采集、索引和可视化,对系统进行进程级别的监控,对系统内部的操作进行策略性的报警、对分布式的rpc调用进行trace跟踪以便于进行性能分析。
github地址:https://github.com/JThink/SkyEye
部分资料:http://blog.csdn.net/column/details/14346.html
可以线下多交流交流
交流群:624054633
我的Email:leviqian@sina.com

springboot-learning-example

开源项目泥瓦匠BYSocket 发表了文章 • 0 个评论 • 864 次浏览 • 2017-06-06 17:29 • 来自相关话题

项目名称:springboot-learning-example
项目地址:https://github.com/JeffLi1993/ ... ample

项目介绍:spring boot 实践学习案例,是 spring boot 初学者及核心技术巩固的最佳实践。
『 基础 - 入门篇 』
springboot-helloworld - 《Spring Boot 之 HelloWorld 详解》
springboot-properties - 《Spring Boot 之配置文件详解》

『 基础 - Web 业务开发篇 』
springboot-restful -《Springboot 实现 Restful 服务,基于 HTTP / JSON 传输》
springboot-freemarker -《Spring Boot 集成 FreeMarker 详解案例》
springboot-validation-over-json -《Spring Boot HTTP over JSON 的错误码异常处理》

『 基础 – 数据存储篇 』
springboot-mybatis -《Springboot 整合 Mybatis 的完整 Web 案例》
springboot-mybatis-annotation -《Spring Boot 整合 Mybatis Annotation 注解的完整 Web 案例》
springboot-mybatis-mutil-datasource -《Spring Boot 整合 Mybatis 实现 Druid 多数据源详解》

『 基础 – 数据缓存篇 』
springboot-mybatis-redis -《Spring Boot 整合 Redis 实现缓存操作》
springboot-mybatis-redis-annotation -《Spring Boot 注解实现整合 Redis 作为缓存》

『 其他篇 』
springboot-elasticsearch -《Spring Boot 整合 Elasticsearch,实现 function score query 权重分查询》
springboot-dubbo-server 
springboot-dubbo-client - 《Springboot 整合 Dubbo/ZooKeeper 详解 SOA 案例》



  查看全部
项目名称:springboot-learning-example
项目地址:https://github.com/JeffLi1993/ ... ample

项目介绍:spring boot 实践学习案例,是 spring boot 初学者及核心技术巩固的最佳实践。
『 基础 - 入门篇 』
springboot-helloworld - 《Spring Boot 之 HelloWorld 详解》
springboot-properties - 《Spring Boot 之配置文件详解》

『 基础 - Web 业务开发篇 』
springboot-restful -《Springboot 实现 Restful 服务,基于 HTTP / JSON 传输》
springboot-freemarker -《Spring Boot 集成 FreeMarker 详解案例》
springboot-validation-over-json -《Spring Boot HTTP over JSON 的错误码异常处理》

『 基础 – 数据存储篇 』
springboot-mybatis -《Springboot 整合 Mybatis 的完整 Web 案例》
springboot-mybatis-annotation -《Spring Boot 整合 Mybatis Annotation 注解的完整 Web 案例》
springboot-mybatis-mutil-datasource -《Spring Boot 整合 Mybatis 实现 Druid 多数据源详解》

『 基础 – 数据缓存篇 』
springboot-mybatis-redis -《Spring Boot 整合 Redis 实现缓存操作》
springboot-mybatis-redis-annotation -《Spring Boot 注解实现整合 Redis 作为缓存》

『 其他篇 』
springboot-elasticsearch -《Spring Boot 整合 Elasticsearch,实现 function score query 权重分查询》
springboot-dubbo-server 
springboot-dubbo-client - 《Springboot 整合 Dubbo/ZooKeeper 详解 SOA 案例》