10 октября 2011 г.

Основы EJB: часть 1

Сервер приложений j2ee состоит из двух основных элементов: контейнер web-приложения (JSP, JSF и т.д.) и EJB-контейнер. Первый служит для создания пользовательского интерфейса и слабо подходит для описания бизнес-логики приложения. Для этого используется вторая часть J2EE - EJB.

Технологию EJB (Enterprise Java Beans) можно рассматривать с двух точек зрения: как фреймворк, и как компонент.
С точки зрения компонента EJB - это всего-лишь надстройка над POJO-классом, описываемая с помощью аннотации. Существует три типа компонентов EJB:
  1. session beans - используется для описания бизнесс-логики приложения
  2. message-driven beans - так же используется для бизнесс-логики
  3. entities - используется для хранения данных
С точки зрения фреймворка EJB - это технология, предоставляющая множество готовых решений (управление транзакциями, безопасность, хранение информации и т.п.) для вашего приложения.

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



Основные архитектуры EJB
Существует 2 основные архитектуры при разработке enterprise-приложений:
  • традиционная слоистая архитектура (traditional layered architecture)
  • domain-driven design (DDD)
Обе эти архитектуры предпологают разделение приложения на функциональные слои, каждый из которых используется для решения задач определенного плана.

К примеру, традиционная слоситая архитектура предпологает разделение приложения на 4 базовых слоя: слой презентации, слой бизнесс-логики, слой хранения данных и непосредственно слой самой базы данных.
Обычно слой презентации реализуется через web-приложение (т.е. используя JSP, JSF, GWT и т.п.) или web-сервис (что дает возможность написания клиента, к примеру, на C#). В нем реализовано взаимодействие с пользователем: формы для получения запросов от пользователя и средства для предоставления ему запрошеной информации. Слой бизнесс-логики является основой для enterprise-приложения. В нем описываются бизнесс-процессы, производится поиск, авторизация и множество других вещей. Слой бизнесс-логики использует механизмы слоя хранения данных. Чем отличается слой хранения данных и слой базы данных? Тем, что в первом описываются высокоуровневые объектно-ориентированные механизмы для работы с сущностями БД, в то время как второй - это и есть непосредственно база данных (Oracle, MySQL и т.п.)


Архитектура DDD предпологает, что объекты обладают бизнесс-логикой, а не являются простой репликацией объектов БД. Многие программисты не любят наделять объекты логикой и создают отдельный слой, называемый service layer или application layer. Он похож на слой бизнесс-логики традиционной слоистой архитектуры за тем лишь отличием, что он намного тоньще.


Типы компонентов EJB
Как уже было сказано выше существует 3 типа компонентов EJB: session beans, message-driven beans и entities.
Рассмотрим каждый из этих компонент немного детальнее.


Session beans
Вызываются пользователем для совершения какой-либо бизнесс-операции. Существует 2 типа session-beans: stateless и stateful.
Stateful-бины автоматически сохраняют свое состояние между разными клиентскими вызовами. Типичным примером stateful-бина является корзина в интернет-магазине.
Stateless-бины используются для реализации бизнесс-процессов, которые могут быть завершены за одну операцию.Так же на основе stateless-бинов проектируются web-сервиса.

Message-driven beans
Так же как и session beans используются для бизнесс-логики. Отличие в том, что клиенты никогда не вызывают MDB напрямую. Обычно сервер использует MDB в асинхронных запросах.


Entities и Java Persistence API
Одним из главным достоинством EJB3 стал новый механизм работы с persistence - возможность автоматически сохранять объекты в реляционной БД используя технологию объектно-реляционного маппинга (ORM).
В контексте EJB3 persistence провайдер - это ORM-фреймворк, который поддерживает EJB3 Java Persistence API (JPA). JPA определяет стандарт для:
  • конфигурации маппинга сущностей приложения и их отображения в таблицах БД;
  • EntityManager API - стандартный API для CRUD (create, read, update, delete) операций над сущностями;
  • Java Persistence Query Language (JPQL) - для поиска и получения данных приложения;
Можно сказать, что session beans - это "глаголы" приложения, в то время как entities - это "существительные".

EntityManager - это инерфейс, который связывает класс сущности приложения и его представление в БД. EntityManager знает как нужно добавлять сущности в базу, одновлять их, удалять, а так предоставляет механизмы для настройки производительности, кэширования, транзакций и т.д.

JPQL - это похожий на SQL язык запросов.

Реализация
Теперь рассмотрим реализацию этих сущностей. В EJB3 мы используем POJO (Plain Old Java Objects), POJI (Plain Old Java Interfaces) и аннотации. Если с первыми двумя всё понятно, то про аннотации стоить поговорить отдельно. Аннотация записывается так:
@<имя аннотации>(<список парамет-значение>)

Какие аннотации могут быть?
  • Stateless - говорит контейнеру, что класс будет stateless session bean. Для него контейнер обеспечит безопасность потоков и менеджмент транзакций. Дополнительно, вы можете добавить другие свойства, например прозрачное управление безопасностью и перехватчики событий;
  • Local - относится к интерфейсу и говорит, что bean реализующий интерфейс доступен локально
  • Remote - относится к интерфейсу и говорит, что bean доступен через RMI (Remote Method Invocation)
  • EJB - применятеся в коде, где мы используем bean.
  • Stateful - говорит контейнеру, что класс будет stateful session bean.
  • Remove - опциональная аннотация, которая используется с stateful бинами. Метод, помеченный как Remove говорит контейнеру, что после его исполнения нет больше смысла хранить bean, т.е. его состояние сбрасывается. Это бывает критично для производительности.
  • Entity - говорит контейнеру, что класс будет сущностью БД
  • Table(name="...") - указывает таблицу для маппинга
  • Id, Column - параметры маппинга
  • WebService - говорит, что интерфейс или класс будет представлять вэб-сервис.
  • и много-много других...
Условия создания session bean
В качестве session bean может выступать обычный класс Java удовлетворяющий следующим условиям:
  • иметь как минимум один метод
  • не должен быть абстрактным
  • иметь конструктор по-умолчанию
  • методы не должны начинаться с "ejb" (например ejbCreate, ejbDoSomething)
Для stateful-бинов существует еще одно условие: свойства класса должны быть объявлены примитивами или реализовывать интерфейс Serializable.
Методы жизненного цикла EJB
У stateless и MDB бинов существует 2 события жизненного цикла, которые мы можем перехватить: создание и удаление бина. Метод, который будет вызываться сразу после создании бина помечается аннотацией javax.annotation.PostConstruct, а перед его удалением - javax.annotation.PreDestroy. Stateful бины обладают помимо рассмотреных выше еще 2 событиями: при активации (javax.ejb.PostActivate) и при деактивации (javax.ejb.PrePassivate).


Особенности stateless и stateful бинов
Один bean может содержать множество клиентских методов. Этот момент является важным для производительности, так как контейнер помещает экземпляры stateless-бинов в общее хранилище и множество клиентов могут использовать один экземпляр бина.
В отличии от stateless stateful бины инстанцируются для каждого пользователя отдельно.


Еще пара слов об интерфейсах
Интерфейс может быть помечен как Local, что сделает классы, реализующие этот интерфейс, классами локальной бизнесс-логики. Локальные интерфейсы не требуют никаких дополнительные действий при реализации.

В противном случае интерфейс может быть помечен как Remote, что обеспечит возможность работы RMI. Обычно такой интерфейс расширяет интерфейс Remote, но это не обязательно.
Если вас интересует функциональность и Local и Remote интерфейсов - вот интересный пример из "EJB 3 in Action":
public interface BidManager{
    void addBid(Bid bid);
    List<Bid> getBids(Item item);
}

@Local
public interface BidManagerLocal extends BidManager {
    void cancelBid(Bid bid);
}

@Remote
public interface BidManagerRemote extends BidManagerLocal {}

@WebService
public interface BidManagerWS extends BidManager {}

Перехватчики
При создании enterprise-приложений часто возникает необходимость записывать лог вызываемых методов (в целях отладки или для лога безопасности), а так же контролировать доступ пользователей к отдельным частям приложения. Для этого используются перехватчики - объекты, методы которых вызываются автоматически при вызове метода EJB-бина. Объект-перехватчик является POJO, за тем лишь исключением, что метод, который должен вызываться автоматически аннотируется @AroundInvoke, например:
public class MyLogger {
    @AroundInvoke
    public Object logMethodEntry( InvocationContext invocationContext ) throws Exception {
         System.out.println("Entering methid: " + invocationContext.getMethod().getName() );
         return invocationContext.proceed();
     }
}
Обратите внимание на возвращаемое значение и параметр функции. В данном случае мы говорим контейнеру, что после вызова перехватчика можно вызывать метод, который он перехватил (или следующий за ним перехватчик), однако мы могли сгенерировать исключение или не вызывать метод proceed() и тогда метод EJB-бина не выполнился бы.

Использовать перехватчики можно двумя путями: указать его применение через аннтоации для каждого класса или метода в отдельности или указать перехватчик по-умолчанию для определенных (или всех) бинов.

Чтобы перехватчик применился только к определенному методу перех декларацией метода можно написать аннотацию @Interceptors( MyLogger.class ). Чтобы перехватчик работал для всех методов бина эту же аннотацию можно было бы написать перед декларацией класса, например:
@Interceptors( MyLogger.class )
@Stateless
public class MyClass { ... }
Можно указывать несколько перехватчиков, тогда их перечисляют через запятую, например:
@Interceptors( MyLogger1.class, MyLogger2.class )
Порядок вызова перехватчиков никак нельзя задать через аннотации, но его можно изменять, если описывать их через дескриптор развертывания.

Один перехватчик описывается так:

    
        *
        com.example.MyLogger
    
Сначала вызывается перехватчик по-умолчанию, потом специфичный для класса, а за ним для метода. Если вы не хотите чтобы для вашего метода вызывался перехватчик по-умолчанию - используйте аннотацию @ExcludeDefaultInterceptors или @ExcludeClassInterceptors.

4 комментария:

  1. Я требую продолжения банкета! Даёшь "Основы EJB: часть 2"

    ОтветитьУдалить
  2. спасибо за краткое и подробное объяснение компонентов EJB. Хотелось бы продолжения :)

    ОтветитьУдалить

Примечание. Отправлять комментарии могут только участники этого блога.