Решатели аргументов метода - отличный инструмент для поддержания чистоты вашего Spring приложения. Реализуя интерфейс HandlerMethodArgumentResolver
, вы можете извлечь логику, необходимую для создания экземпляра настраиваемого аргумента для компонента и поддерживать чистоту ваших контроллеров.
Но в некоторых ситуациях использование преобразователей аргументов метода может привести к неидеальной производительности.
О чем я говорю
Я покажу вам на примере:
- Допустим, у нас есть приложение, которое получает настраиваемый заголовок с JSON-представлением объекта контекста.
- Мы будем разрешать различные аргументы, используя этот объект контекста.
- В какой-то момент мы обнаружим некоторые вещи, которые нужно выполнить более одного раза.
Мы можем начать пример кода с написания преобразователя для самого ContextInformation
:
Этот компонент разрешает ContextInformation
из заголовка с именем X-App-Context
. Если этот преобразователь правильно зарегистрирован (подробнее о регистрации преобразователей см. В моей статье о преобразователях аргументов), мы можем написать контроллер для тестирования преобразователя:
Мы можем протестировать наш контроллер и резольвер с помощью httpie:
Хорошо, все выглядит хорошо, если предположить, что наш ContextInformation
выглядит примерно так:
Наш аргумент разрешен правильно, и наш контроллер может использовать настраиваемый аргумент, не загрязняя свой код созданием экземпляра ContextInformation
.
Все идет нормально.
Эта проблема
Теперь мы хотим добавить новый преобразователь для нового типа аргумента. И нам нужен контекст приложения, чтобы его получить:
На данный момент мы выполняем почти одну и ту же задачу дважды. Конечно, мы можем провести рефакторинг обоих преобразователей, чтобы избежать повторения кода, но десериализация JSON, содержащегося в заголовке, будет выполняться несколько раз, если оба преобразователя будут выполнены. Давайте напишем метод контроллера, чтобы это произошло:
В этом примере выполняются оба преобразователя, а JSON десериализуется дважды. В этом случае это может показаться не таким уж большим количеством лишней работы. Но повторная обработка может быть намного дороже в реальных сценариях.
Решение проблемы с фильтрами
Мы можем использовать фильтры и атрибуты запроса, чтобы избежать этой проблемы: мы обрабатываем заголовок в фильтре, а затем, после десериализации JSON, мы сохраняем десериализованный объект как атрибут запроса.
Этот метод является настолько старомодным, насколько это возможно, поддержка атрибутов запроса присутствует в API сервлета с момента первой спецификации сервлета.
Напишем наш фильтр:
В этом случае мы используем OncePerRequestFilter
, который представляет собой реализацию фильтра с поддержкой Spring, которая срабатывает только один раз за запрос. OncePerRequestFilter
предоставляет шаблонный метод doFilterInternal
, который мы можем переопределить, чтобы реализовать поведение фильтра.
В этом примере мы игнорируем обработку ошибок и предполагаем, что заголовок всегда присутствует и правильно сформирован.
Учитывая это предположение, мы знаем, что у нас будет атрибут, содержащий должным образом instantiatedContextInformation
для каждого запроса, который достигает нашего сервера.
Мы можем изменить наши резолверы, чтобы воспользоваться этим фактом:
Решатель для ContextInformation
теперь сведен к одной значащей строке кода. То же самое и с UserInformationtResolver
:
Немного рефакторинга
Теперь решение работает правильно, но мы можем сделать немного лучше: оба преобразователя должны знать имя атрибута, выполнять приведение и т. Д.
Мы можем написать служебный компонент:
С этим RequestUtils
наш фильтр теперь может быть реализован следующим образом:
И наши финальные резолверы выглядят чище:
Я загрузил пример кода в свою учетную запись Github, чтобы упростить отслеживание и запуск примеров.
Подведение итогов
Используя старые добрые фильтры для установки атрибутов, мы можем упростить нашу работу в разных частях нашей архитектуры. В этой истории мы говорили о резолверах, но этот трюк также можно использовать для поддержания чистоты других частей нашей системы.
О подобной технике для АОП мы поговорим в ближайшее время.
Будьте на связи!