Java web: от сокетов в сервлеты в spring web

Допустим мы хотим написать web-server с rest-like API на java, но пока не знаем никаких фреймворков, стандартов и библиотек.

1. Socket

Первое, что мы можем сделать - реализовать полностью сами всю логику с нуля на сокетах.

Socket - это такое понятие из TCP протокола. Это некое гнездо, которое может принимать/отправлять пакеты данных по сети на определенном порту.

Нам нужно будет сделать примерно следующее:

* установка/завершение TCP соединений с клиентами

* поддержание пула соединений, чтобы сервер мог параллельно обрабатывать несколько запросов

* разбор http хедеров, кук, урлов

* и много других низкоуровневых механизмов, предполагающих изучение документации протоколов и сложного concurrency

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

В добавок бизнес-логика сервера скорее всего утонет в техническом коде, что еще больше усложнит поддержку.

Пример общения клиента с сервером по TCP протоколу
Пример общения клиента с сервером по TCP протоколу

2. Servlet API

Чтобы решить проблемы выше был изобретен стандарт Servlet API.

Самая главная часть API методе service() интерфейса javax.servlet.Servlet:

public void service(ServletRequest req, ServletResponse res)

Нам нужно всего лишь достать необходимые параметры / хедеры / и т д из параметра req и записать ответ / статус в res.

В нашем случае намного легче взять абстрактную реализацию этого интерфейса - HttpServlet

Примерно так:

Код класса ru.zen.java.notes.HelloServlet
Код класса ru.zen.java.notes.HelloServlet

И привязать этот сервлет к какому нибудь пути в XML:

XML конфигурация WAR-файла
XML конфигурация WAR-файла

Теперь, если открыть в браузере http://my-api.com/hello , увидим текст "Hello from servlet"

Ответ сервера в браузере
Ответ сервера в браузере

А как это работает?

Всю магию делает за нас «контейнер сервлетов». Это как раз та штука, которая реализует спецификацию сервлетов и предоставляет нам Servlet Api.

Самые распространенные:

* Tomcat

* Jetty

* Undertow

А схема примерно такая:

1. Мы пакуем наш код в специальный формат war и загружаем в контейнер

2. На каждый запрос к серверу по указанному пути сервер создает новый сервлет с нашим кодом

3. На каждый запрос выделяется собственный поток

Контейнер сервлетов обрабатывает запросы клиента
Контейнер сервлетов обрабатывает запросы клиента

Этот подход уже значительно упрощает разработку веб-сервера, но остались еще пара важных вещей, которые фреймворк мог бы делать за нас:

• сериализация/десериализация тела запроса и ответа (например, json <–> pojo)

• более гибкий маппинг урлов в методы (роутинг)

• и ещё куча более специфичных хотелок от безопасности, до транзакционности

Spring framework vs Jakarta EE

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

• Spring Framework

• Jakarta EE (ex Java EE)

Jakarta EE

Jakarta EE - это огромное количество стандартов и API типа JTA, CDI, JPA, JNDI и еще много-много. Эти api позволяют упростить разные спектры разработки веб-приложений.

Если контейнер сервлетов заодно реализует еще все эти стандарты, он называется "сервер приложений". Примеры:

• WildFly (ex JBoss)

• WebLogic (by Oracle)

• WebSphere (by IBM)

• TomEE (Tomcat c реализацией всех тех стандартов)

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

Spring Framework

В основе экосистемы спринга стоит DI. Это такая штука, которая позволяет удобно подсовывать одни объекты в другие. Ядро оказалось очень удобным и вокруг него выросла куча библиотек и фреймворков. Большинство из них имеют аналог в мире Jakarta EE. Примеры:

• spring web

• spring jdbc template

• spring security

• spring cloud

• spring data

Как работает spring framework?

1. Мы пишем код в виде простых классов (называются бинами bean)

2. Пишем код, который описывает какие бины нужно заинджектить в какие

3. Запускаем IoC контейнер спринга

4. IoC контейнер читает наш код и конфигурацию

5. По указаниям в конфигурации подсовывает инстансы одних классов в другие

6. Мы достаем из него нужные классы и запускаем на них методы

Как выбрать?

Jakarta EE в основном используют всякие интерпрайзы (типа банков) потому что они любят больших вендоров (типа IBM и Oracle) и море XML.

Хипстеры же предпочитают spring, потому что он больше для людей.

Конечно, это все вкусовщина. Я надеюсь, что в ближайшем будущем Jakarta EE еще составит достойную конкуренцию spring. Но покодив на обоих, я для себя выбрал spring. Поэтому дальше рассказ про него.

Spring Web

Так вот Spring Web - это веб фреймворк, построенный поверх Servlet API, чтобы еще больше упростить нам жизнь.

Пример кода:

Код класса ru.zen.java.notes.HelloController
Код класса ru.zen.java.notes.HelloController

Этот класс называется контроллер. Его методы соответствуют урлам нашего api. Когда клиент запросит http://my-api.ru/hello , выполнится метод getHello().

Мы описали все это с помощью аннотаций. Spring Web прочитает их и настроит сервлет как нужно.

Происходит это примерно так:

1. Спринг распарсил наши контроллеры и настроил специальный класс DispatcherServlet

2. На каждый запрос запускается DispatcherServlet

3. Он определяет, какой вызвать метод в каком контроллере

4. В нашем методе контролера отрабатывает бизнес логика

5. Ответ сериализуется (например в json)

Обработка запроса в Spring Web
Обработка запроса в Spring Web

Теперь мы всем довольны и можем реализовывать свое API, не думая ни о чем кроме бизнес логики.

Выводы:

1. Почти никогда не имеет смысла реализовывать логику веб-фреймворка самостоятельно - это долго и сложно

2. Большинство веб-фреймворков работает поверх Servlet API

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