[Перевод] Java EE 8: краткий и весьма оптимистичный обзор новых возможностей
Автор снимка — Сабине Хюрдлер / Fotolia.com
Привет, Хабр!
Когда-то давно, знойным летом 2013 года вы и именно вы убедили нас взяться за работу над замечательной книгой «Изучаем Java EE 7» Энтони Гонсалвеса, которая выдержала 6 тиражей и стала настоящим бестселлером. Теперь мы всерьез рассчитываем еще до конца года приступить к работе над книгой по Java EE 8 от грамотного и симпатичного специалиста Себастьяна Дашнера.
7 ноября господин Дашнер опубликовал статью с собственными размышлениями о перспективах и плюшках Java EE 8. Под катом вашему вниманию предлагается перевод этой статьи с немецкого языка
На чем следует сконцентрироваться при разработке проектов для больших предприятий? В чем именно заключается «легкость» платформы Java EE? Каким образом Java EE поддерживает бизнес-концепции и предметно-ориентированное проектирование? Как развивалась эта платформа? В каком направлении продолжает развиваться? В этой статье кратко рассмотрены новые возможности и интеграция Java в контейнерные окружения
Разработка: акцент на Enterprise-проектах
Ключевая составляющая любых Enterprise-программ — это такие функции, которые дают оборот средств. Добавленная стоимость приобретается при реализации кейсов, обеспечении бизнес-логики, в меньшей степени зависит от того, как именно выполнена техническая реализация, либо какая технология для этого выбрана. Таким образом, Enterprise-проекты должны быть заточены прежде всего на реализацию практических сценариев.
Разработчик зачастую видит эту ситуацию иначе. Его интересуют детали реализации, качество, а также красота технических решений. Программа проектируется, обсуждается с коллегами, код перерабатывается и структурируется. Да, все это важные аспекты разработки ПО — в любом случае, в первую очередь нас интересует выполнение требований заказчика, а уже потом придется тратить время и силы на доработку кода.
Когда enterprise-инженер научится видеть эти проблемы с точки зрения заказчика (выкладывающего деньги) и с точки зрения проект-менеджера, у которого сверстан бюджет — многое станет понятнее.
Java Enterprise — первым делом бизнес-логика!
Поэтому мы уделяем особое внимание реализации бизнес-логики (логики предметной области). Лишь когда логика будет готова, затрагиваются более узкие технические вопросы. Java Enterprise благоприятствует такому подходу. Предметные области — его суть — сначала реализуются на чистом Java. Поскольку мы работаем в enterprise-секторе, для нас весьма актуальны и сквозные функциональности, такие, как коммуникация и сохраняемость данных. На современной Java EE они вполне реализуются декларативно, практически с минимальными издержками. В принципе, на платформе Java EE действует принцип «соглашений по конфигурации», поддерживающий такой подход к программированию.
Спецификации Java добавляются к нашей бизнес-логике (классам на чистом Java) при помощи немногочисленных аннотаций. Затем бизнес-логика с легкостью проверяется при помощи модульных тестов. Поскольку на современной Java EE почти весь функционал декларируется в аннотациях, классы можно инстанцировать прямо в тестах — в том числе, без enterprise-контейнера.
Java EE и предметно-ориентированное проектирование (DDD)
Мы реализуем входную точку конкретного кейса, так называемый фасад, как аннотированный Java-класс. В терминологии предметно-ориентированного проектирования принято говорить, что такой класс представляет некоторый сервис. В следующем примере показан сервис, реализованный как EJB:
@Stateless
public class CarManufacturer {
@Inject
CarFactory carFactory;
@PersistenceContext
EntityManager entityManager;
public Car manufactureCar(Specification spec) {
Car car = carFactory.createCar(spec);
entityManager.persist(car);
return car;
}
}
Класс CarFactory
создает новые сущности Car
согласно имеющимся спецификациям. «EntityManager» управляет сохраняемостью этих сущностей в базе данных.
Таким образом представлена концепция, известная в предметно-ориентированном проектировании как паттерн «репозиторий». Тип «EntityManager«предлагает совершенно четкий API. Дополнительный фасад, также именуемый DAO (объект доступа к данным), применявшийся в мире J2EE, здесь более не нужен.
При представлении бизнес-процессов на уровне программы их сначала нужно реализовывать прямо в виде фасада, как CarManufacturer
в данном случае. Если методы и процессы чрезмерно усложнятся, функционал переходит на уровень делегатов, таких, как CarFactory
в данном случае. Модульные тесты обеспечивают верное поведение — в этих тестах инстанцируются фасады, а делегаты, соответственно, имитируются в виде mock-объектов.
Классы предметной области, к примеру, Car
, в то же время являются POJO («plain old Java object»), которые можно снабжать аннотациями, иллюстрирующими технически целесообразный функционал, например, сохраняемость:
@Entity
@Table(name = "cars")
public class Car {
@Id
@GeneratedValue
private long id;
@OneToOne(optional = false, cascade = CascadeType.ALL)
private Engine engine;
@OneToMany(cascade = CascadeType.ALL)
private Set seats = new HashSet<>();
// геттеры и сеттеры
}
В предметно-ориентированном проектировании под сущностями понимаются конкретные объекты, относящиеся к предметной области; это «коренные» объекты нашего приложения. JPA обеспечивает отображение сущностей на уровень сохраняемости почти без дополнительных издержек — это делается при помощи аннотаций. Обозначение аннотации @Entity
также гармонично вписывается в мир DDD. JPA требует, чтобы @Entity
можно было идентифицировать по @Id
.
JPA также поддерживает концепцию под названием агрегат. Так, продолжая наш пример, в машине найдется несколько сидений. В нашей предметной области можно идентифицировать каждое сиденье. Операции сохранения каскадируют от корневого объекта Car ко всем относящимся к нему экземплярам Seat. Таким образом, мы можем запросить из базы данных по имеющемуся идентификатору целую машину, включая все ее составные элементы.
DDD-фабрики, инкапсулирующие в методах или классах создание сравнительно сложных объектов, реализуются в мире Java EE либо на чистом Java, где методы — это части сущностей предметной области, либо при помощи CDI. Что касается CDI, отдельного упоминания заслуживают их фабричные методы (producer method) — очень компактные и полезные. В следующем примере сначала показана фабрика, часть сущности «автомобиль»:
public class Car {
...
public LogBook createDriverLog() {
// создаем инструкцию logbook
}
}
Упаковывать фабрики в сущности целесообразно прежде всего в тех случаях, когда для создания конкретных объектов соответствующая сущность должна обладать многочисленными свойствами. В следующем примере показано создание производного CarFactory
при помощи CDI:
public class CarFactoryProducer {
@Produces
public CarFactory exposeCarFactory() {
CarFactory factory = new BMWCarFactory();
// применяем специальную логику
return factory;
}
}
Как и в первом примере, экземпляр BMWCarFactory
можно внедрить при помощи типа CarFactory
.
В этих примерах показано, насколько легковесный подход к разработке практикуется в современной Java EE, не влекущий существенных технических издержек. Основное внимание уделяется бизнес-логике, которая без особых издержек адаптируется под технические требования Enterprise-приложения.
Коммуникация в Java EE: JAX-RS, JMS, WebSocket и JSON
Серверные конечные точки с JAX-RS, JMS или WebSocket — другие примеры, иллюстрирующие, как тонкие декларативные API облегчают жизнь разработчику. В большинстве прикладных ситуаций вполне достаточно действующего в Java EE принципа «соглашение по конфигурации». В иных случаях поведение можно расширять программно.
JSON-связывание при помощи JSON-B — еще одно нововведение, появившееся в Java EE 8. Как и при применении JAXB для XML, JSON-B позволяет декларативно отображать классы Java на JSON и обратно. В Java EE 8 JSON-B бесшовно интегрируется с JAX-RS; JAX-RS Runtime делегирует JSON-отображение ссылочных типов к JSON-B. Таким образом, классы тех типов, что применяются в ресурсах JAX-RS, могут определять аннотации. Без каких-либо дополнительных конфигурационных издержек эти аннотации учитываются в HTTP-запросах и откликах. В следующем примере показан класс User
, сериализуемый при помощи JSON-B:
public class User {
@JsonbTransient
private long id;
@JsonbProperty("username")
private String name;
...
}
JAX-RS-ресурс, представляющий конечную точку HTTP в данном прикладном случае, задает для тела запроса или отклика тип "User"
:
@Path("users")
@Produces(MediaType.APPLICATION_JSON)
public class UsersResource {
@Inject
UserStore userStore;
@GET
public List getUsers() {
return userStore.getUsers();
}
}
Сериализация JSON-B подхватывает все автоматически: в JSON-объектах переданного по HTTP списка пользователей содержится свойство username
и отсутствует id
.
Декларативный подход и взаимодействие с API Java EE особенно хороши при Enterprise-разработке, поскольку в этой сфере всегда требуется сосредоточиться только на самом существенном.
Java EE и приложения с нулевыми зависимостями: работа с Docker и Kubernetes
Тот факт, что программисту требуется работать максимально продуктивно, также отражается и на уровне модели сборки и развертывания. В приложениях Java EE применение традиционно отделяется от реализации. Классы импортируют только лишь API — реализацию обеспечивает Enterprise-контейнер. Такой подход позволяет максимально сократить время сборки, доставки и развертывания. На всех этапах работы конвейера непрерывной доставки компилируется, собирается и доставляется лишь содержимое приложение –, но не реализация каркаса. Последняя происходит уже во время исполнения.
Именно поэтому Java EE максимально выгодно раскрывается в мире контейнеров и оркестровки, то есть, в связке с Docker и Kubernetes. Когда в файловой системе применяется копирование при записи и действуют отдельные кэши на уровнях отдельных образов контейнеров, инфраструктура выполняет лишь самые необходимые операции. При каждой итерации доставляются лишь скомпилированные классы нашего приложения, но не технологическая часть. Этот подход обеспечивают так называемые «приложения с нулевыми зависимостями», упаковываемые в WAR-архив. При этом приложение содержит зависимости лишь с областью видимости provided
.
В следующем примере показан файл Docker, содержащий базовый образ GlassFish 5 и приложение Java EE. Потенциальные зависимости приложения не доставляются в развертываемую версию, а фигурируют лишь на более ранних этапах в Docker-файле.
FROM oracle/glassfish:5.0
# добавляем потенциальные provided-зависимости
COPY …jar $GLASSFISH_HOME/glassfish/domains/domain1/lib/ext/
COPY target/cars.war $GLASSFISH_HOME/glassfish/domains/domain1/autodeploy
Получаемый в результате образ Docker при запуске автоматически выполняет наше приложение.
Благодаря приложениям с нулевыми зависимостями, а также этим элементам из файла Docker реализуется концепция «копирование при записи», значительно ускоряющая весь конвейер непрерывной доставки. Если не изменяется ни среда времени исполнения, ни зависимости, существующие между несколькими сборками (а так обычно и бывает), то упаковываются и пересылаются только лишь классы нашего приложения в архиве cars.war
.
Как правило, в Enterprise-проектах зачастую приходилось хотя бы отчасти иметь дело со сложностями, версиями и конфигурацией, касающимися серверов приложений. На серверах находится множество приложений, что осложняет развертывание, конфигурирование и новые операции установки, а в некоторых случаях также требует согласования. Такая проблема решается при помощи контейнеров вроде Docker. Поскольку в контейнере содержится все, что необходимо для запуска и конфигурирования приложения — в том числе, среда времени исполнения и конфигурация — выбор технологии не зависит от других приложений. Обновление версий, например, переход с Java EE 7 на Java EE 8 получается не столь обременительным и рискованным. Поэтому в развертываемой версии не приходится доставлять весь стек целиком и мегабайты зависимостей — все это входит в состав соответствующих базовых образов.
Java EE 8 и далее
Итак, в Java EE 8 появились новые возможности, среди которых наиболее заметен новый стандарт JSON-B, значительно упрощающий Enterprise-разработку. Ранее за улаживание конфликтов версий между сторонними зависимостями и, в частности, их транзитивными зависимостями и библиотеками серверов приложений отвечали фреймворки для отображения JSON, например, Jackson. На разрешение конфликтов уходили ресурсы и время. С Java EE 8 можно обойтись без всех этих лишних издержек.
Кроме JSON-B был введен еще один новый стандарт — Security API, обеспечивающий безопасность, а также были обновлены некоторые старые API: CDI, JSON-P, Bean Validation, JPA, Servlet и JSF. Кроме того, особо отмечу такие нововведения, как события Server-Sent, реактивные клиенты в JAX-RS, новые возможности JSON-P 1.1. и Bean Validation 2.0, а также многочисленные доработки в интеграции различных стандартов. Направление, в котором развивались API Java EE в последние месяцы и годы, весьма оптимистично с точки зрения разработчиков, а после передачи платформы Java EE в ведение Eclipse Foundation (встречайте Eclipse Enterprise for Java (EE4J)) дальнейшая разработка Java EE должна пойти в более открытом режиме и, вероятно, ускориться.
Итог
Современная Java EE позволяет программисту сосредоточиться на самых важных прикладных аспектах и снизить до минимума технические издержки. Поскольку готовые к развертыванию версии стали максимально компактными, Java EE теперь оптимально интегрируется с контейнерными окружениями — например, Docker, Kubernetes или OpenShift.