предисловие
Недавно я использовал Spring Boot с MyBatis, плагином Universal Mapper и плагином подкачки PageHelper для нескольких небольших и средних API-проектов.После этого я чувствую, что эта структура и инструменты действительно удобны для разработки таких проектов, и реакция команды тоже хорошая. В процессе построения и развития проекта я также подытожил небольшой опыт и поделился им с вами.
Перед разработкой API-проекта не нужно говорить об основных задачах сборки проекта, внедрения зависимостей и настройки фреймворка.Обычно для того, чтобы ускорить ход разработки проекта (иди домой пораньше), необходимо инкапсулировать некоторые часто используемые классы и инструменты, такие как унифицированные результаты ответов.Инкапсуляция, унифицированная обработка исключений, аутентификация подписи интерфейса, базовая инкапсуляция методов добавления, удаления и модификации, базовые инструменты генерации кода и т. д. могут быть запущены только с этими проектами.
Тем не менее, в следующий раз, когда вы будете делать аналогичный проект, возможно, придется выполнить описанные выше шаги снова.Хотя это обычно берется и модифицируется, это все равно пустая трата времени. Таким образом, вы можете использовать идею объектно-ориентированной абстракции и инкапсуляции, чтобы извлечь общие черты таких проектов и инкапсулировать их в начальный проект (по оценкам, у большинства компаний будет много подобных начальных проектов), чтобы подобные проекты будет разработан в следующий раз. Повторяйте непосредственно исходный проект, уменьшая бессмысленное дублирование работы.
После того, как связанный проект был запущен, мне потребовалось некоторое время, чтобы упростить начальный проект, и я поделился проектом на GitHub. Если вы планируете сделать аналогичный проект, вы можете клонировать его и попробовать. Адрес проекта и документация по использованию:GitHub.com/Ли Хэнмин/…. Если вы обнаружите проблемы в использовании или у вас есть хорошие предложения, отправьте сообщение о проблеме или 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 в соответствии с именем таблицы (по умолчанию предоставляются два набора шаблонов Controller, POST и RESTful.
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)
- Мой Батис (Посмотреть официальную китайскую документацию)
- Плагин универсального картографа MyBatisb (Посмотреть официальную китайскую документацию)
- Плагин подкачки MyBatis PageHelper (Посмотреть официальную китайскую документацию)
- Друид Весенняя загрузка Стартовый (Посмотреть официальную китайскую документацию)
- Фастсон (Посмотреть официальную китайскую документацию)
послесловие
Спасибо за вашу поддержку. Я не ожидал, что простое резюме проекта и обмен информацией привлекут внимание стольких людей всего за два дня, и он также был в списке трендов GitHub. Я надеюсь, что, если у вас есть время, вы сможете принять участие в ряды обмена открытым исходным кодом, делитесь знаниями, с удовольствием кодируйте и поощряйте друг друга.