Рубрики
Без рубрики

Java Enterprise 101

Существует множество способов создания программного обеспечения. На самом деле, их даже очень много… С тегами java, java enterprise, jee, spring.

Существует множество способов создания программного обеспечения. На самом деле, существует даже множество способов создания хорошего программного обеспечения. Когда дело доходит до разработки серверов приложений, один из них выдержал испытание временем, и на то есть веская причина: Java Enterprise Edition.

Java EE – это гораздо больше, чем просто библиотека программного обеспечения. Это также архитектура и философия. JEE не для слабонервных; если вы создаете одноразовый прототип, вы пошли не тем путем. Но если вы пришли почитать об архитектуре, которая может обслуживать крупные предприятия и поддерживать масштабные приложения, то вы на правильном пути. JEE – это тяжелая артиллерия разработки программного обеспечения. И это чертовски круто.

В этой статье мы рассмотрим архитектурную сторону JEE. Скоро последует статья о внедрении.

Небольшое примечание. Когда я говорю о Java Enterprise в этой статье, я имею в виду не конкретно библиотеку, а архитектуру и философию. Официальные библиотеки Java EE – это всего лишь один из способов реализации этого стека.

Итак, вы готовы к серьезной разработке программного обеспечения? Отбросьте любые нетипизированные языки, причудливые сценарии, разработку, основанную на шумихе, и хипстерские технологии, давайте станем серьезными и напишем программное обеспечение, которое будет работать в течение следующих 20 с лишним лет. Вот архитектура Java EE в двух словах:

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

  • Каждое общение с внешним миром строго основано на запросе-ответе .
  • Входящий запрос проходит через несколько слоев , прежде чем он достигнет вашего кода приложения. Каждый слой может отказаться , перенаправить или изменить просьба. Мотивацией, лежащей в основе этих слоев, является разделение проблем .
  • Многие из слоев уже реализованы и вам просто нужно ими воспользоваться.
  • JEE – это все, что позволяет разработчикам сосредоточиться только на бизнес-функциональности . 99% всего остального было сделано за вас.

Когда запрос поступает на наш серверный компьютер по сети, он сначала передается в операционную систему . Операционная система определит, какому приложению переслать запрос, на основе порта оно было отправлено по адресу. В этом случае приложением является Виртуальная машина Java . JVM внутренне запускает контейнер приложений (например, Apache Tomcat или Стеклянная рыба ). Контейнер приложения реализует Java Servlet API . Контейнер приложения выполняет несколько функций:

  • Он управляет одним или несколькими приложениями, которые доставляются в виде сервлетов . На практике большинство контейнеров содержат только один сервлет, но теоретически один Tomcat может содержать произвольное количество сервлетов.
  • Он предоставляет реализацию servlet API. Это позволяет содержащимся приложениям взаимодействовать с контейнером. Наиболее заметным использованием этой функции является создание цепочки фильтров (подробнее об этом позже).
  • Он обеспечивает интеграцию с Services API операционной системы. Таким образом, приложение, запущенное внутри контейнера, может быть запущено, завершено и перезагружено как служба уровня операционной системы. По этой причине, несмотря на то, что Java является мультиплатформенной, многие контейнеры приложений содержат код, специфичный для конкретной платформы (поэтому содержащиеся в них приложения остаются независимыми от платформы).
  • Он перенаправляет входящие запросы в правильное приложение на основе сопоставления путей . Нередко можно увидеть один зарегистрированный сервлет для статического содержимого (привязанный к /static ) и один для динамического API вашего сервера приложений (привязанный к /api ). Он управляет пулом потоков для запросов и привязывает каждый запрос к потоку. Поскольку поток содержит
  • контекст запроса , не рекомендуется вручную запускать новые потоки в среде JEE (если вы не знаете именно то, что вы делаете).

Традиционно приложения Java EE развертываются с помощью архивных файлов, известных как WAR files (для W eb AR chive) или EAR files (для E nterprise Архив ). Внутренняя файловая структура этих архивов стандартизирована. Контейнеры приложений извлекают содержащиеся файлы при запуске и запускают содержащиеся сервлеты. При этом контейнер привязывает ваш сервлет к указанному порту (либо указанному в коде, либо в файле конфигурации).

Обычно при работе в архитектуре JEE вы не будете реализовывать все с нуля. Многие задачи абсолютно одинаковы от одного приложения JEE к другому, поэтому имеет смысл использовать подходящий фреймворк. В основном, это фактическая эталонная реализация JEE и Spring framework. Я не могу много сказать о “ванильном” JEE, так как до сих пор я использовал исключительно Spring framework. Мы обсудим это более подробно в следующей статье.

Каждый входящий запрос, прежде чем он будет передан вашему приложению, должен пройти серию так называемых Фильтров сервлетов , которые образуют цепочку фильтров . Как только запрос проходит первый фильтр, включается второй фильтр и так далее. У каждого фильтра есть возможность заблокировать запрос. Контейнеры приложений позволяют настраивать цепочку фильтров с помощью Servlet API. Реализации JEE framework используют цепочку фильтров для многих задач, включая управление сеансами и безопасность. Фильтры также могут иметь побочные эффекты; если есть задача, которую вы хотите выполнить для каждого запроса, вы часто увидите реализацию в виде фильтра сервлета. Кроме того, если вам нужно привязать некоторую информацию к самому запросу, фильтры сервлетов являются обычным местом для этого.

Уровень представления – это то место, где ваш фактический код приложения впервые отвечает входящему запросу. Этот запрос прошел цепочку фильтров сервлетов, поэтому сеанс пользователя настроен и готов к работе, и вся аутентификация уже выполнена. В первые дни JEE уровень представления был местом, где происходила генерация HTML-страниц на стороне сервера. В настоящее время уровень представления состоит из набора контроллеров REST, которые предлагают различные конечные точки, составляющие ваш REST API. Если вы сталкиваетесь со старыми приложениями, вы также столкнетесь с веб-службами XML на уровне представления. Обычно на уровне представления выполняется проверка пользовательского ввода на стороне сервера и общая проверка запросов. Точно так же, как вы никогда не должны писать SQL-запросы в своем графическом коде, уровень представления не должен пытаться получить прямой доступ к базе данных. Классу на уровне представления разрешено взаимодействовать только с другим классом уровня представления, классом уровня обслуживания или элементом модели данных, который был возвращен уровнем обслуживания.

Уровень сервиса – это то место, где находится ваш фактический код приложения. Это место для того, чтобы воплотить ваши бизнес-правила в код. Сервисный уровень – это место, где вы перемещаете данные в своей модели данных, создаете новые элементы, удаляете старые и т.д. В зависимости от вашего варианта использования уровень сервиса может быть таким же маленьким, как “перенаправить этот вызов на уровень хранилища”, или чрезвычайно сложным процессом. Классы уровня сервиса могут взаимодействовать только с другими службами или с классами уровня репозитория.

Это последний уровень в вашем коде, который изменяет ваши данные перед тем, как они попадут в базу данных. Преобладающим элементом на этом уровне являются Репозитории (также известные как Объекты доступа к данным или DAO*s). Эти классы просто предлагают ряд методов, которые позволяют вам * сохранять , загружать , удалять и запрос ваши данные в базе данных. Здесь важно то, что вы никогда не должны позволять какой-либо специфике вашего хранилища данных выходить за пределы уровня репозитория – сама его цель состоит в том, чтобы убедиться, что вы можете обменять хранилище данных на другое (возможно, даже базу данных SQL с хранилищем NoSQL!). Внутренне ваши методы репозитория будут содержать фактические инструкции запроса. Если вы работаете со стандартным стеком JEE, то у вас будет Java Persistence API (JPA) Провайдер, такой как Hibernate на месте. JPA позволяет вам конвертировать вашу модель домена в таблицы SQL и обратно с относительной легкостью. В нем все еще есть много подводных камней, и он заслуживал бы отдельной статьи. Как вы, наверное, уже догадались, классы уровня репозитория не вызывают никаких других классов за пределами своего собственного уровня, за исключением классов JPA.

Модель данных представляет данные в вашем домене . Это единственный архитектурный элемент, который будет использоваться всеми тремя слоями вашего приложения. Поэтому крайне важно , чтобы классы модели предметной области не имели НИКАКИХ ссылок на какие-либо другие классы, за исключением классов, которые находятся внутри самой модели предметной области. В отличие от классов уровня представления, обслуживания и сохранения, модель предметной области с сохранением состояния . Как правило, вы не хотите иметь много логики в модели предметной области; в основном она существует для хранения ваших данных и предоставления чистого API, фактические сложные изменения выполняются на бизнес-уровне. Модель предметной области, хотя и не требуется явно в JEE, почти всегда следует шаблону JavaBean. Правильные геттеры и сеттеры здесь не обсуждаются, если вы хотите использовать стандартные фреймворки для простой обработки вашей модели предметной области, такие как проверка Bean и JPA (подробнее об этом позже). Элемент модели предметной области – это ваш типичный POJO – частные поля, конструктор, а также геттеры и сеттеры. Обычно такие фреймворки, как JPA, Jackson и JAXB, дополнительно заставляют вас предоставлять каждому классу конструктор по умолчанию, потому что эти классы должны быть созданы с помощью Java reflection. В отличие от почти всех других классов в архитектуре JEE, имеющих чистую реализацию equals() и hashCode() является решающим для POJOS модели предметной области. Обычно для этой цели каждый элемент модели предметной области имеет уникальный идентификатор, который также совпадает с его идентификатором в таблицах базы данных.

/| Запрос всегда привязан к потоку в JEE, который создается и управляется контейнером приложения (обычно в пуле потоков). Это означает, что серверное приложение JEE всегда по своей сути является параллельным, вы не можете избежать этого. Как мы все знаем, правильно работать с параллелизмом сложно . К счастью, архитектура JEE помогает вам, когда дело доходит до параллелизма. Если вы посмотрите на картинку выше, вы увидите четырех пользователей, работающих с приложением параллельно, каждый из которых представлен запросом/ответом, привязанным к потоку. Стоит отметить одну конкретную деталь: потоки никогда не пересекаются . Приложение не выполняет синхронизацию, а вместо этого предоставляет ее компоненту, который действительно хорош в этом: базе данных.

Как это возможно? Как мы можем иметь все эти уровни над базой данных без необходимости учитывать многопоточность? Вспомните, когда параллелизм становится проблемой: когда несколько потоков обращаются к одним и тем же данным. Вы хотите избежать этого случая любой ценой в приложении JEE (есть исключения, такие как кэши на уровне приложения). Для этого все классы, принадлежащие уровню репозитория и уровню сервиса , являются без состояния в JEE. У них нет полей, ни частных, ни общедоступных, которые удерживали бы изменяемое состояние. Так что же насчет данных? Данные загружаются для каждого пользователя и по запросу . Когда запрос поступает на уровень сервиса (уровень представления здесь является небольшим исключением), открывается новая транзакция базы данных для исключительного использования этим пользователем. Затем службы собирают запрошенные данные и/или выполняют запрошенные изменения, и все это в рамках одной транзакции. Прежде чем результат будет передан на уровень представления, транзакция фиксируется и закрывается.

Эта архитектура имеет два больших преимущества:

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

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

О ДЖИ можно было бы сказать гораздо больше. Я часто чувствую, что она получает много незаслуженной критики просто потому, что ее неправильно понимают. Он действительно хорошо сочетается с современными стилями и языками программирования и помогает создавать очень стабильные приложения. В некотором смысле, JEEP – это не столько то, что он предоставляет вам как программисту, сколько то, от чего он защищает вас (проблемы с параллелизмом, проблемы с целостностью данных, …). Архитектура JEE является ярким примером защитного программирования в этом отношении – в первую очередь речь идет о безопасности. Эта архитектура зарекомендовала себя как хорошо подходящая для крупных проектов и команд.

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

Оригинал: “https://dev.to/martinhaeusler/java-enterprise-101-3djl”