Как создать подходящий веб-фреймворк?

Архитектура

доВывод фреймворка веб-разработкиВ этой статье мы шаг за шагом построили среду разработки.

如何搭建合适的Web框架?

В условиях того времени это все еще отвечало потребностям. Однако с постепенным улучшением проекта частота изменения требований постепенно становится выше частоты появления новых требований, а недостатки исходного фреймворка становятся все более очевидными, поэтому фреймворк нуждается в обновлении и улучшении.

Давайте сначала рассмотрим проблемы исходного фреймворка, а затем усовершенствуем фреймворк на основе этих проблем.

Проблемы с оригинальной рамой

  • проблема с генерацией кода
  • проблема с передачей параметров
  • Проблема сервисного уровня
  • Проблемы с тестовой зависимостью
  • Проблемы с Mapper.xml

проблема с генерацией кода

В исходной структуре мы написали компонент генерации кода на основе различных ограничений.С помощью этого компонента мы можем создать ряд классов, таких как контроллер, сервис, модель, картограф и т. д. для выбранной таблицы, то есть как пока таблица построена, вы можете напрямую создать набор CRUD, который можно запустить и протестировать напрямую. Это выглядит красиво в начале проекта, но когда требования меняются, остается много ограничений.

Во-первых, логика сгенерированного кода закрепляется. Если есть небольшая корректировка, то нужно корректировать компоненты, которые генерируют код, затем перепаковывать, загружать на склад jar, дорабатывать компонентную версию проекта, а потом генерировать код, слишком громоздкий весь процесс.

Во-вторых, чтобы облегчить генерацию кода, на самом деле было сделано много компромиссов:

  • Чтобы облегчить регенерацию полей таблицы после их изменения, многие классы абстрагируют базовый класс для управления полями модели. Эти базовые классы нельзя изменить вручную, поскольку они перезаписываются при каждой сборке. Это фактически приводит к увеличению количества классов.
  • Сгенерированный CRUD затвердевает и не может быть скорректирован вручную. Если сгенерированный CRUD не соответствует требованиям, его нельзя изменить непосредственно в коде. Вы можете сделать копию только для модификации, потому что она будет перезаписана при повторном создании. Это приводит к избыточности кода.
  • Param и Result делегируют Модель, поэтому, когда Модель изменяется, настройка соответствующих полей может быть известна во время компиляции. Но это также создает множество проблем, которые мы обсудим отдельно в разделе «Проблемы с передачей параметров».

проблема с передачей параметров

Для облегчения генерации кода было решено, что как параметр, так и результат наследует от модели, что привело к следующим проблемам:

  • Заставьте Param и Result полагаться на Model. Но Param и Result являются моделями уровня представления, а Model — моделью постоянного уровня, и эволюция обоих несовместима. Однако текущие отношения наследования привели к эволюционным потребностям и постоянству модели уровня представления по умолчанию.Конечно, вы также можете вручную настроить Param и Result, но это также привело к преимуществам генерации кода.
  • Поля Param и Result задаются путем делегирования, то есть у них фактически нет полей, а значения задаются Модели через геттеры и сеттеры. Это делает невозможным использование ломбока для упрощения геттеров и сеттеров, что приводит к большему количеству строк кода Param и Result.
  • В то же время для swagger некоторые аннотации должны быть основаны на полях, поэтому некоторые функции не могут быть реализованы (например: ModelAttribute), а могут обрабатываться только на основе дополнительных средств (например: ApiImplicitParams нужно использовать для реализации полевые документы).
  • CRUD основан на тех же Param и Result, что приводит к тому, что внешний интерфейс отображает множество бесполезных полей, что затрудняет понимание внешнего интерфейса интерфейсом.

Проблема сервисного уровня

Уровень службы имеет следующие проблемы:

  • Обязанности сервисного слоя слишком тяжелыми, включая обработку транзакций, настройки параметров и бизнес-логику.

  • В результате код в Сервисе представляет собой спагетти-код, не способствующий пониманию бизнес-логики.

  • В то же время аннотация транзакции добавляется непосредственно в класс. Механизм транзакций Spring по умолчанию заставит логические вызовы, подобные следующему коду, не вызывать ожидаемое исключение.

    // Почтовая служба общественная строка savePost (сообщение) { postRepository.save(сообщение); for(PostDiscuss обсудить : post.getDiscuss()) { // Исключение RuntimeException здесь нельзя поймать, это будет исключение TransactionRollBack обсудитьService.save(обсудить); } } // обсудитьСервис public String savePost(PostDiscuss обсудить) { выбросить новое исключение RuntimeException("Ошибка сохранения"); }

Проблемы с тестовой зависимостью

Основная бизнес-логика находится в Сервисе, и тест по-прежнему должен полагаться на Spring.Когда проект становится все больше и больше, время запуска проекта становится все больше и больше, что может занять 1 минуту или даже больше. Это делает модульное тестирование все менее и менее эффективным.

Проблемы с Mapper.xml

Во время интервью я часто задаю следующие вопросы:

  • Какова роль интерфейсов в Java?
  • Почему Service и DAO должны писать интерфейс, а затем реализовывать этот интерфейс?
  • Интерфейс и реализация находятся в одном модуле, и их все равно придется переупаковывать. Разве написание более одного интерфейса не требует еще нескольких строк кода?
  • Подобно проблеме выше, в Mybatis утверждается, что sql разделен на файл Mapper.xml, так что sql может быть изменен напрямую без компиляции. Но Mapper.xml ставится вместе с Class, и его все равно нужно переупаковывать, если он изменен, а Mybatis не может динамически загружать Mapper.xml, так в чем преимущества разделения sql на XML?

Мой ответ на последний вопрос заключается в том, что для большинства проектовнет преимущества!Простота развертывания и расширения проекта зависит не от используемого вами фреймворка, а от вашего дизайна..

Что касается Mapper.xml, то Mybatis отделяет sql от кода, но если все же положить Mapper.xml и код в один модуль в проекте, то это преимущество пропадает. Поскольку такого преимущества нет, нам все равно нужно отдельно писать файл Mapper.xml? Мой выбор — не писать его и использовать аннотации, предоставленные Mybatis напрямую.

В то же время, чтобы решить сильную зависимость уровня службы от уровня DAO (здесь, Mybatis), в структуру были внесены некоторые улучшения для разделения уровней службы и DAO. Подробности смотрите в плане улучшения ниже.

Программа улучшения структуры

Чтобы решить вышеуказанные задачи, структура была скорректирована следующим образом:

  • Отдельный параметр, результат и модель
  • генерация кода замены
  • независимая бизнес-логика
  • Оптимизация слоя модели

Отдельный параметр, результат и модель

Как упоминалось выше, будет много проблем с сильной связью между Param, Result и Model, поэтому здесь мы разделим Param, Result и Model. Каждый из них является независимым компонентом, который решает вышеуказанные проблемы. Но появляются две новые проблемы:

  • Во-первых, очевидно, увеличивается объем ручного кодирования. Когда таблица изменяет поле, ей необходимо изменить три или более классов.
  • Во-вторых, был добавлен код между передачами данных. То есть при передаче Param в Модель необходимо присвоить значение полю. Если установлено значение одного поля, это добавит много скучного кода. И использование отражения будет иметь некоторое влияние на производительность.

Так как же решить эти две проблемы? Во-первых, чистая ручная мастурбация определенно невозможна. Необходимо предусмотреть некоторые средства автоматизации.

Для назначения Spring предоставляет BeanUtils для упрощения обработки.Хотя значение устанавливается на основе отражения, для текущего этапа эта потеря производительности не влияет. Однако BeanUtils не может копировать атрибуты разных типов.Предположим, у меня есть объект Domain Book, в котором есть поле Author, и теперь я хочу присвоить его BookResult, в котором есть поле AuthorResult.В настоящее время BeanUtils не может присвоить значение. Поэтому я написал инструментальный класс на основе Gson для проведения теста производительности.Для копирования свойств BeanUtils 10 000 раз требуется более 500 миллисекунд, а инструментальному классу на основе Gson требуется всего около 300 миллисекунд.

Для генерации полей таблицы, если используется IDEA, IDE по умолчанию предоставляет скрипт, который может генерировать POJO из таблиц! Мы можем использовать этот скрипт для генерации модели, а затем скопировать поля в Param и Result, чтобы упростить написание полей. Я изменил этот скрипт, чтобы он соответствовал потребностям проекта. В основном добавлена ​​поддержка ломбока, а также добавлены аннотации классов и аннотации полей.

генерация кода замены

Для проблемы вышеуказанного компонента генерации кода я скорректировал способ генерации кода. Он больше не создается на основе компонентов, а основывается на собственных FileTemplate, LiveTemplate и Scripted Extensions IDEA. Хотя этот метод не может генерировать несколько файлов одновременно, поскольку логика генерации в основном одноразовая, влияние не очень велико. При генерации кода в первый раз эффективность компонента генерации кода выше, чем комбинация FileTemplate, LiveTemplate и Scripted Extensions, но гибкость последующей настройки явно выше, чем комбинация FileTemplate, LiveTemplate и Scripted Extensions, чем код Компонент генерации:

  • Во-первых, когда структура файла настроена, необходимо только изменить FileTemplate и экспортировать файл конфигурации членам команды проекта.
  • Точно так же, когда LiveTemplate настроен, необходимо только изменить соответствующий LiveTemplate и экспортировать файл конфигурации членам команды проекта.
  • Во-вторых, какой файл вы хотите сгенерировать, просто сгенерируйте его для этого файла
  • В-третьих, после создания полного файла с помощью FileTemplate вы можете быстро выполнять модульное кодирование с помощью LiveTemplate.
  • Наконец, FileTemplate может быть установлен на уровне проекта, т. е. каждый проект может иметь независимый FileTemplate.

Конкретный рабочий процесс показан ниже.

независимая бизнес-логика

Для задач Сервиса и тестирования исходные три уровня Контроллер, Сервис и Модель делятся на четыре уровня:

  • Контроллер отвечает за получение и возврат внешних данных, а также за унифицированную обработку исключений.
  • Сервис отвечает за транзакции и сборку логики доменного уровня. Здесь не будет проблемы с вложенностью транзакций, и это не вызовет проблемы с незахватом ожидаемого исключения.
  • Домен отвечает за бизнес-логику
  • Модель отвечает за сохранение данных

Таким образом, обязанности Сервиса уменьшаются, и больше не возникает проблема вложенности транзакций.

Оптимизация слоя модели

Как упоминалось выше, Mapper.xml был окончательно заброшен в фреймворке, и вместо него для реализации операций сохранения использовались аннотации Mybatis. Вместо этого использование аннотаций позволяет избежать написания XML-кода, но не устраняет сильную зависимость фреймворка от Mybatis. Поэтому к домену добавляется уровень интерфейса репозитория.Этот уровень используется для определения операции постоянства домена, а репозиторий реализуется на уровне модели.Реализацией здесь является реализация Mybatis. В этом есть два преимущества:

  • Инверсия зависимости: Выяснилось, что Домен зависит от слоя Модели, но теперь слой Модель зависит от слоя Домена, поэтому, когда я хочу заменить Mybatis, Домен совершенно не в курсе.
  • Независимое тестирование: Поскольку теперь Домен не зависит ни от какого другого уровня, его можно протестировать без базы данных и контейнера. Чтобы эффективность тестирования не снижалась по мере развития проекта

如何搭建合适的Web框架?

Детали улучшения фреймворка

Теперь, когда мы знаем, как улучшить структуру, мы можем начать вносить изменения. По сути, основная трансформация — это трансформация метода генерации кода, то есть написание FileTemplate, LiveTemplate и ScriptedExtensions. Ниже приводится краткое описание этих трех функций, начиная с ScriptedExtensions.

Scripted Extensions

Давайте сначала объясним, что такое Scripted Extensions. Все мы знаем, что текущие IDE являются подключаемыми, то есть мы можем разрабатывать подключаемые модули с помощью комплектов разработки подключаемых модулей, предоставляемых разработчиками для расширения существующих функций IDE. Однако для написания плагина требуется определенная среда разработки, а если это очень простая функция, настроить среду разработки очень проблематично. Таким образом, IDEA предоставляет скриптовые расширения, которые можно понимать какУпрощенная версия плагина, то есть вы можете расширить функциональность IDE через скрипты.

IDEA предоставляет функцию базы данных, которая может подключаться к базе данных для связанных операций. Когда вы подключаетесь к базе данных и щелкаете правой кнопкой мыши по таблице, вы можете увидеть параметр Scripted Extensions, который имеет функцию для создания groovy-скриптов POJO на основе таблицы.

Но функция относительно низкая:

  • Имя пакета жестко закодировано: com.sample
  • аннотации таблиц не создаются
  • Он не основан на ломбоке, чтобы упростить геттер и сеттер.

К счастью, мы можем изменить его на основе этого скрипта.В меню Scripted Extensions только что есть опция Go to Scripts Directory.Нажав, вы можете войти в каталог скрипта.

如何搭建合适的Web框架?

Просто Ctrl+c, Ctrl-v, сделайте копию этого отличного файла, переименуйте его и измените на основе этого скрипта. В частности, как изменить в соответствии со своими потребностями, которые в основном основаны на сращивании строк в соответствии с информацией таблицы.

FileTemplate

FileTemplate — это шаблон для создания файлов, предоставленный IDEA.После того как вы нажмете File->New... в меню, различные файлы, которые появляются, основаны на FileTemplate. Наши пользовательские классы Controller, Service, Domain и другие классы могут быть упрощены с помощью FileTemplate.

Конкретный метод использования: нажмите Ctrl-Alt-S, чтобы вызвать меню настроек, нажмите «Редактор» -> «Файл и шаблон кода» и добавьте в него шаблон.

如何搭建合适的Web框架?

Несколько заметок:

  • В следующем описании перечислены некоторые параметры по умолчанию и их функции.
  • Вы также можете настроить переменную.Если настраиваемой переменной не присвоено значение, будет поле ввода для запроса ввода при ее создании.
  • Шаблоны основаны на Velocity, поэтому, если вы знакомы с Velocity, вы можете сразу приступить к работе.
  • Параметр Enable Live Template активирует переменную LiveTemplate в FileTemplate, но ее необходимо обернуть #[[]]#. Но для создания Java эта функция имеет баги и не может найти нужную позицию, поэтому пока не используется.

После создания вы можете увидеть шаблон в меню «Создать».

LiveTemplate

LiveTemplate на самом деле является CodeSnippet. Метод создания аналогичен FileTemplate. Нажмите Ctrl-Alt-S, чтобы вызвать меню настроек, нажмите Editor->Live Template и добавьте в него шаблон.

如何搭建合适的Web框架?

Несколько заметок:

  • Переменные здесь обернуты с помощью $$
  • Каждая переменная является заполнителем, после использования табуляции для расширения вы можете вручную ввести значение
  • Редактировать переменные в правом нижнем углу используется для присвоения значений переменным IDEA предоставляет некоторые методы, а также может устанавливать значения по умолчанию.
  • По следующей ссылке изменения вы можете выбрать место, где LiveTemplate вступает в силу, например, он вступает в силу только при объявлении класса Java.

процесс кодирования

После создания вышеуказанных шаблонов процесс кодирования выглядит следующим образом:

  • Щелкните правой кнопкой мыши таблицу и сгенерируйте модель с помощью скриптовых расширений.
  • Для быстрого создания контроллера, службы, домена и других категорий с помощью FileTemplate
  • Быстро пишите код с LiveTemplate

Суммировать

Этот документ обновляет фреймворк, разбирая и решая проблемы исходного фреймворка, чтобы адаптировать его к разработке и продвижению проекта.