У меня была возможность поработать с сервисом API Java и Spring RestFul для моего проекта. Spring — одна из самых известных платформ, которая наиболее широко используется для корпоративных веб-приложений на языке java.

Тем не менее, мне пришлось страдать и бороться, чтобы получить четкое представление о фреймворке и лучших практиках по сравнению с другими языковыми фреймворками, которые я использовал до сих пор (nodeJS/Express, Gin/Golang). Возможно, у Spring высокая кривая обучения, но было очень трудно наверстать упущенное в условиях ограниченного времени разработки. Мне пришла в голову идея поделиться своими мыслями и трудностями по поводу Spring и получить возможность узнать разные мнения. Кроме того, я надеюсь, что эти мысли помогут вам выбрать правильный фреймворк для вашего java-проекта.

Трудно получить общую архитектуру фреймворка из официальной документации.

Понимать и следовать официальной документации — один из самых важных моментов в использовании фреймворка. Для весны вы должны получить всю необходимую информацию с https://spring.io/guides.

Попробовал прочитать и удивился огромности фреймворка! Он состоит из множества подфреймворков/библиотек. Например…

  • Весенняя безопасность
  • Весенний МВК
  • Весна JPA
  • Весенний аудит
  • Весенний ботинок
  • Весенний ХАТЕОАС
  • более….

Когда я смогу закончить, чтобы понять все тогда? Даже для кратких обзоров компонентов? Мы можем проверить, что такое основные проекты Spring, но я все еще не уверен, какой модуль мне следует использовать для моего проекта…

Конечно, хорошо, что один фреймворк охватывает множество функций. Более того, он, кажется, хорошо задокументирован. Однако было бы также полезно, если бы они могли предоставить простой обзор, который поможет нам легко найти подходящие модули. В нашем случае мы не могли использовать функции Spring Audit, потому что просто не могли обнаружить, что они находятся в проекте Spring Data. Мы реализовали все вручную (потому что не знали), а после того, как нашли, рефакторить было уже поздно...

Слишком много предопределенных реализаций. Какой из них подходит для моих нужд?

Как мы видели в предыдущей главе, Spring — это огромный фреймворк. Это влияет не только на документацию, но и на реальную реализацию, которую мы пишем.

Например, моему проекту Spring требовалась простая базовая аутентификация. Весной это заняло почти 5 дней, так как мне нужно было понять правильную реализацию. Во-первых, концепция фильтра Spring, затем XML-конфигурация Spring Security (класс конфигурации Spring основан на использовании этого XML, поэтому мы должны знать как фундаментальное, наконец, Конфигурация как код для пользы аннотаций >.

С другой стороны, с узлами/экспрессом у меня ушло всего 10 минут! Я только что установил библиотеку через npm и добавил ее в качестве нового промежуточного программного обеспечения. Довольно просто и интуитивно понятно! Вы можете проверить наличие nodeJS.

Также для моего проекта мне нужно было реализовать пользовательскую аутентификацию HTTP-заголовка. С этой целью я расширил интерфейс «GenericFilter», чтобы он работал вместе со стандартной базовой аутентификацией. В ходе исследования я нашел один абстрактный класс фильтра под названием «AbstractAuthenticationProcessingFilter». Судя по названию, он казался подходящим для пользовательских нужд авторизации. Однако оказалось, что полезной информации нет… Я не мог начать, не зная, как правильно это реализовать. Как я могу переопределить методы интерфейса? Для чего существует интерфейс? Много основных вопросов, но нет четкого ответа (вместо того, чтобы зацикливаться на вопросах о переполнении стека..)

Я перечислил конкретные вопросы в виде фрагментов кода. Вы можете ознакомиться с примером реализации здесь.

  1. Конструктор

2. метод «попытка авторизации»

3. Метод «дофильтровать»

Аннотации затрудняют сохранение одного канонического способа реализации

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

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

Например, Spring предлагает собственные модули для модульного тестирования веб-приложений. Есть хороший туториал о том, как его запустить. Для модульного тестирования RESTFul API я использовал класс org.springframework.test.web.servlet.MockMvc.

Итак, из учебника это выглядит примерно так:

Здесь класс «MockMvc» был автоматически внедрен «@Autowired».

С другой стороны, реализации модульных тестов членами нашей команды были такими:

Мы не использовали «@Autowired», вместо этого инициализировали его вручную перед запуском каждого модульного теста (аннотация «@Before»)! В моем проекте два разных способа работают абсолютно одинаково. Так почему же существуют эти две разные реализации? В чем разница?

Хорошо, может быть, я и все члены нашей команды новички и не знали лучших практик Spring. Тем не менее, ИМХО, я предпочитаю фреймворки, которые предоставляют нам, возможно, простые правила и способы подвести нас к правильной реализации.

Несогласованное поведение между подмодулями

У меня была еще одна проблема, когда я писал весенние модульные тесты. Как объяснялось, в моем проекте есть расширенный общий фильтр, который выполняет аутентификацию пользовательского заголовка HTTP, и мне нужно было применить эту аутентификацию только для пути URL-адреса API. Как я могу это сделать? Я сообщил, что Spring boot имеет сервлет «по умолчанию» (класс «DispatcherServlet» из модуля Spring MVC), и он перенаправляет входящие запросы каждому конкретному классу контроллера, который мы реализуем.

Для сервлета «по умолчанию» путь сервлета — это URI запроса минус путь контекста, а информация о пути равна нулю. Это общее правило сервлета. Я мог бы использовать этот путь сервлета, а затем проверить, должны ли входящие запросы быть авторизованы или нет. Мое загрузочное приложение Spring работает точно так, как ожидалось.

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

[ПРИМЕЧАНИЕ] Обратите внимание, что я использую «MockMvcRequestBuilders» для генерации запросов GET и использую «MockMvcResultMatchers» для получения содержимого ответа и т. д.…

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

Таким образом, между модульными тестами и приложением Spring Boot существует непоследовательное поведение. Как мы можем решить эту проблему?

Опять я застрял, чтобы найти причины и взял его на некоторое время. В конце концов я нашел объяснение JavaDoc о «MockHttpServletRequestBuilder.servletPath».

Specify the portion of the requestURI that represents the path to which the Servlet is mapped. 
This is typically a portion of the requestURI after the context path. In most cases, tests can be written by omitting the servlet path from the requestURI. 
This is because most applications don't actually depend
on the prefix to which a servlet is mapped. For example if a Servlet is mapped to {@code "/main/*"}, tests can be written with the requestURI
{@code "/accounts/1"} as opposed to {@code "/main/accounts/1"}.
If specified here, the servletPath must start with a "/" and must not end with a "/".
@see javax.servlet.http.HttpServletRequest#getServletPath()

Таким образом, чтобы генерировать запросы для модульных тестов, мы передаем весь URL-адрес запроса в «MockMvcRequestBuilders.get()», но этот URL-адрес запроса не будет использоваться в качестве пути сервлета, а будет использоваться информация о пути по умолчанию. Если вы хотите настроить это поведение, вам необходимо явно указать путь к сервлету!

Я не знаю, как пришло это решение и почему… но эти крошечные каверзные проблемы доставляют нам неприятности и мешают быстрой и быстрой доставке (особенно для разработчиков, у которых нет опыта работы со Spring).

Вывод

Вы можете проверить все примеры в этом посте с образцом Репозиторий Github. Кроме того, я нашел несколько интересных сообщений, в которых упоминаются похожие проблемы, которые я перечислил здесь. Особенно мне очень понравился последний средний пост (в котором сравнивались Spring и Gin (Golang)).









Спасибо, что нашли время прочитать!