В данной статье рассмотрим EJB — Enterprise JavaBeans. Данная технология является частью спецификации Java EE.
Мы коснемся таких вопросов, как:
- что такое EJB;
- какова история возникновения EJB;
- какие есть типы EJB.
Краткая история EJB
В далеком 1996 году, когда автору данной статьи было 5 лет, Java уже пользовалась популярностью среди разработчиков. Причиной тому были дружелюбный API, автоматическая сборка мусора, и т.д. Java широко использовалась в системах, отвечавших за бэкенд. Однако, несмотря на все прелести языка, программисты того времени нуждались в определенном функционале, еще не реализованном в JDK. Такими потребностями были:- обеспечение персистентности данных;
- целостность транзакций
- конкурентный доступ к данным (управление многопоточностью);
- и, скорее всего, что-то еще.
Что такое EJB
EJB в некотором смысле — собирательный термин, который в зависимости от контекста может подразумевать под собой либо саму технологию Enterprise JavaBeans в общем, либо некоторый конкретный программный компонент (бин) Enterprise JavaBean, который является частью технологии EJB. Определение EJB как технологии приводится на википедии: Enterprise JavaBeans (также часто употребляется в виде аббревиатуры EJB) — спецификация технологии написания и поддержки серверных компонентов, содержащих бизнес-логику. Является частью Java EE. Эта технология обычно применяется, когда бизнес-логика требует как минимум один из следующих сервисов, а часто — все из них:- поддержка сохранности данных (persistence): данные должны быть в сохранности даже после остановки программы. Чаще всего достигается с использованием базы данных;
- поддержка распределенных транзакций;
- поддержка параллельного изменения данных и многопоточность;
- поддержка событий;
- поддержка именования и каталогов (JNDI);
- безопасность и ограничение доступа к данным;
- поддержка автоматизированной установки на сервер приложений;
- удалённый доступ.
Типы EJB
Резюмируем. EJB — это обычный Java класс, отмеченный одной из специальных аннотаций. Такие классы называют бинами. В зависимости от того, какой аннотацией отмечен класс, он будет являться представителем того или иного типа EJB (бинов). Есть три основные типа бинов:- Message Driven Beans (бины, управляемые сообщениями);
- Entity Beans (объектные бины) — определены в спецификации JPA (Java Persistence API) и используются для хранения данных;
- Session Beans (cессионные бины).
- stateless (без состояния);
- stateful (с поддержкой текущего состояния сессии);
- singleton (один объект на все приложение; начиная с версии EJB 3.1).
Session Beans
Session Beans, или сессионные бины — определенный вид бинов. Они инкапсулируют в себе бизнес-логику, которую клиент может программно вызвать посредством вызова методов этого бина. Вызов метода может выполнить:- локально, другим классом в той же JVM, что и сессионный бин;
- удаленно, по сети, из другой JVM, с помощью технологии Java RMI (Remote Method Invocation).
@Stateless
.
Приведем пример ниже:
import javax.ejb.Stateless;
@Stateless
public class StatelessEjbExample {
public String sayHi() {
return "Hi, I'm Stateless EJB!";
}
}
Сессионные бины с поддержкой текущего состояния сессии (Stateful) сохраняют информацию о своем состоянии между обращениями к нему от одного и того же клиента и завершают свое существование по явному запросу от клиента. Достигается это за счет того, что stateful бины уникальны для каждого клиента.
Пример задачи, за которую может отвечать такой тип бинов — поддержка в актуальном состоянии корзины покупок в интернет-магазине для каждого пользователя. Жизненным циклом данных бинов управляет EJB контейнер. Данные бины также уничтожаются, когда клиент завершает свою работу.
Подобные бины тоже довольно просто создавать. Это Java класс, помеченный аннотацией Stateful
. Пример ниже:
import javax.ejb.Stateful;
@Stateful
public class StatefulEjbExample {
public String sayHi() {
return "Hi, I,m Stateful EJB";
}
}
Сессионные бины одиночки (singleton) инициируются один раз за время существования приложения и существуют все время "жизни" приложения. Такие бины разрабатываются для ситуаций, в которых одно состояние должно быть разделено между всеми клиентами.
Подобно stateless бинам, в бинах одиночках разработчику необходимо следить за организацией потокобезопасной среды внутри бина.
Приведем пример Singleton бина, который также прост в создании как и его собратья, речь о которых шла выше. Нетрудно догадаться, что это Java-класс с аннотацией @Singleton
. Однако в данном случае необходимо быть внимательным. Есть две аннотации, идентичные по синтаксису, но различные по назначению и расположенные в разных пакетах:
- javax.ejb.Singleton
- javax.inject.Singleton
javax.ejb
. Пример ниже:
import javax.ejb.Singleton;
@Singleton
public class SingletonEjbExample {
public String sayHi() {
return "Hi, I'm Singleton EJB!";
}
}
Message Driven Beans
Message Driven Beans, или MDB, или бины управляемые сообщениями, подобно сеансовым бинам реализуют некоторую бизнес-логику. Но в отличие от своих родственников, у MDB есть одно важное отличие. Клиенты никогда не вызывают методы MDB напрямую. Такие бины чаще всего выступают в роли слушателей JMS (Java Message Service) сообщений и служат для организации асинхронного обмена сообщениями между частями системы. Примером такого сообщения может быть запрос на доставку товарных запасов от автоматизированной системы розничной торговли к системе управления поставками. Ниже приведем пример MDB бина. В отличие от сессионных бинов, его создание немного интереснее:
import javax.annotation.Resource;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
@MessageDriven(mappedName = "jms/TestQueue")
public class MessageDrivenEjbExample implements MessageListener {
@Resource
private MessageDrivenContext messageDrivenContext;
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
TextMessage msg = (TextMessage) message;
msg.getText();
}
} catch (JMSException e) {
messageDrivenContext.setRollbackOnly();
}
}
}
Аннотация MessageDriven
делает наш класс MDB бином. Внутри аннотации с помощью JNDI (читайте про JNDI тут) определяется имя JMS рассылки, слушателем которой становится наш класс.
Помимо этого, наш класс реализует интерфейс MessageListener
и его метод onMessage
. Данный метод будет вызван, когда придет некоторое сообщение из очереди/рассылки с именем, определенным внутри аннотации MessageDriven
.
Entity beans
Частью технологии EJB является JPA спецификация. JPA, или Java Persistence API — это спецификация, которая обеспечивает объектно-реляционное отображение (ORM — Object-Relational Mapping) Java объектов (Entity бинов) и предоставляющая API для сохранения, получения и управления такими объектами. JPA позволяет представлять данные из БД в виде Java-объектов, а также сохранять Java-объекты в виде записей в базе данных. В роли подобного объекта может выступать не каждый класс, а как раз-таки Entity бины. Entity Bean — это Java класс, который является отображением некоторой таблицы в базе данных. Отображение (маппинг) достигается за счет использования специальных аннотаций. С их помощью осуществляется сопоставление Java-класса с таблицей в базе данных, а также сопоставление полей Java-класса c полями таблицы БД. Приведем пример Entity бина, c комментариями в коде:
@Entity // Делает данный класс Entity бином
@Table(name = "employee") // "Связывает" данный класс с таблицей employee в БД
public class Employee implements Serializable {
@Id // Говорит о том, что поле ниже является первичным ключом
@GeneratedValue(strategy = GenerationType.AUTO) // Определяет тип генерации значений первичного ключа
private int id;
@Column(name="name") // "Связывает" поле ниже с полем name в таблице employee в БД
private String name;
@Column (name="age") // "Связывает" поле ниже с полем age в таблице employee в БД
private int age;
// getters and setters...
}
Стоит отметить, что данный тип бинов имеет смысл изучать только в контексте изучения спецификации JPA.
Пишем приложение: EJB HelloWorld
В данном разделе мы напишем небольшое Java EE HelloWorld приложение, которое развернем на сервере GlassFish. Перед прочтением данной статьи настоятельно рекомендуется прочитать статью о настройке локального окружения.Создаем новый Maven проект в IntelliJ IDEA.
File -> New -> Project...
Нажимаем Next.
Заполняем параметры Maven проекта:
Нажимаем Finish
Проект создан и имеет следующую структуру:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.javarush.lectures</groupId>
<artifactId>ejb_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
</dependencies>
</project>
Далее можно приступать к Java-коду. Наше приложение будет наипростейшим. У нас будет 1 сервлет и 1 EJB. Это будет сессионный бин без сохранения состояния (stateless). Внутри EJB мы определим всего 1 метод, который будет возвращать строку “Hello World”.
Первым делом создадим пакет com.javarush.lectures
.
Затем, внутри пакета com.javarush.lectures
, создадим наш бин — DemoEJB. Код бина приведен ниже:
import javax.ejb.Stateless;
@Stateless
public class DemoEJB {
public String helloWorld() {
return "Hello world!";
}
}
Как было сказано ранее, все довольно просто.
Наш следующий шаг — создать сервлет, который будет передавать значение из EJB в качестве ответа на HTTP-запрос. Стоит отметить, что сервлеты не относятся к теме данной статьи, но для демонстрации EJB все же придется их использовать.
Для этого создадим новый сервлет DemoServlet
в том же пакете, что и EJB. Его код ниже:
@WebServlet("/helloWorld")
public class DemoServlet extends HttpServlet {
@EJB
private DemoEJB ejb;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write(ejb.helloWorld());
}
}
Приведем небольшие комментарии к коду.
Аннотация @WebServlet("/helloWorld")
— определяет наш класс как сервлет, который будет обрабатывать HTTP запросы на эндпоинт /helloWorld
.
У нашего класса есть одно поле — DemoEJB ejb
. Это наш бин, определенный ранее. Аннотация над полем класса — @EJB
осуществляет инъекцию зависимости (DI). Т.е. Переменная ejb автоматически инициализируется новым экземпляром, когда это потребуется.
Наш класс является наследником HttpServlet и переопределяет один из методов суперкласса — doGet
.
Данный метод обрабатывает HTTP GET запросы и принимает в себя два параметра — HttpServletRequest
и HttpServletResponse
. HttpServletRequest
служит для получения информации о поступившем HTTP запросе. HttpServletResponse
нужен для формирования ответа на запрос.
Внутри метода мы получаем объект PrintWriter
у объекта ответа (HttpServletResponse
), с помощью метода getWriter()
. Далее мы можем записать в полученный объект некоторое значение с помощью метода write
. Чем, собственно мы и пользуемся, записывая в объект PrintWriter
-а значение, полученное из определенного нами EJB (значение — строка “Hello World!”). Данное значение клиент, отправивший HTTP-запрос, получит в качестве ответа на свой запрос.
Следующий шаг — запуск приложения на Java EE сервере GlassFish. Для этого создадим новую конфигурацию, как это описано в статье о настройке локального окружения.
Ниже скрин готовой конфигурации для текущего проекта. Убедитесь, что у вас установлен сервер GlassFish перед запуском:
После создания конфигурации запуска — запускаем приложение с помощью меню Run -> Run ‘ejb_demo’ либо с помощью хоткея Shift+F10.
После запуска можно увидеть его логи:
А также открывшийся браузер:
Все это говорит о том, что приложение работает, как задумывалось.
Заключение
В данной статье мы знакомились с EJB — Enterprise JavaBeans. Рассмотрели такие вопросы, как:- Что такое EJB?
- История EJB
- Различные типы EJB
- Message Driven Beans (бины, управляемые сообщениями);
- Entity Beans (объектные бины) — определены в спецификации JPA (Java Persistence API) entities и используются для хранения данных;
- Session Beans (cессионные бины):
- stateless (без состояния)
- stateful (с поддержкой текущего состояния сессии)
- singleton (один объект на все приложение; начиная с версии EJB 3.1)
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ