В этом посте мы рассмотрим несколько основных примеров, чтобы понять, как использовать EJB API.
Основной EJB
EJB — это в основном POJO с некоторыми дополнительными метаданными EJB. Необходимые метаданные для его развертывания в качестве компонента EJB могут быть предоставлены как с помощью аннотаций EJB, так и с помощью стандартного дескриптора развертывания. Следующий класс реализует очень простой EJB сеанса без сохранения состояния:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
package es.reacts; import javax.ejb.Stateless; @Stateless (name = "UniqueLocalSessionEJB" ) public class UniqueLocalSessionEJBBean implements UniqueLocalBusinessInterface { public UniqueLocalSessionEJBBean() { } public String sayLocalHello() { return this .getClass().getName() + "::" + "Local hello." ; } } |
Как вы, возможно, помните из нашего предыдущего поста в блоге, аннотация @Stateless используется для определения сессионного компонента без сохранения состояния. Необязательный элемент name используется для определения имени сессионного компонента. Этот элемент аналогичен элементу <ejb-name /> стандартного дескриптора развертывания. По умолчанию для этого элемента используется неквалифицированное имя класса компонента ( UniqueLocalSessionEJBBean в приведенном выше примере), а в приведенном выше примере он используется для переименования компонента в UniqueLocalSessionEJB .
Поскольку мы используем аннотацию @Stateless , больше нет необходимости объявлять EJB в дескрипторе развертывания.
В этом примере мы предполагаем, что EJB упакован в модуль EJB, который зависит от модуля, содержащего определение его бизнес-интерфейса (как объяснено в следующем разделе).
Бизнес интерфейсы
Каждый EJB реализует один или несколько бизнес-интерфейсов. Бизнес-интерфейсы могут быть локальными или удаленными . Наиболее важные различия между двумя типами бизнес-интерфейсов можно суммировать следующим образом:
- Локальные бизнес-интерфейсы используют семантику передачи по ссылке для своих методов, и вызовы методов не могут пересекать границу JVM. Локальные бизнес-интерфейсы доступны только для вызывающих абонентов в одном приложении и экземпляре JVM вызываемого абонента.
- Удаленные бизнес-интерфейсы используют семантику передачи по значению для своих методов, и вызовы методов могут пересекать границу JVM. Удаленные бизнес-интерфейсы доступны для вызывающих абонентов вне приложения вызываемого абонента.
В предыдущем примере бизнес-интерфейс UniqueLocalBusinessInterface был объявлен следующим образом:
1
2
3
4
5
6
7
8
|
package es.reacts; import javax.ejb.Local; @Local public interface UniqueLocalBusinessInterface { String sayLocalHello(); } |
В мире EJB v. 3.0 бизнес-интерфейс — это просто старый Java-интерфейс с аннотацией @Local или @Remote .
Упаковка бизнес-интерфейсов
В этом примере мы предполагаем, что бизнес-интерфейс EJB упакован в файл JAR, от которого зависит модуль EJB. Поскольку клиенты EJB зависят только от бизнес-интерфейса EJB, рекомендуется упаковать бизнес-интерфейсы в отдельную библиотеку, чтобы упростить распространение интерфейса и отделить их от их реализаций.
Внедрение EJB в сервлет Java
Теперь, когда мы определили EJB, мы готовы использовать его из сервлета в веб-модуле Java EE. Предполагая, что только один EJB-компонент реализовал UniqueLocalBusinessInterface в нашем приложении, мы можем внедрить его с помощью пустой аннотации @EJB :
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
package es.reacts; import java.io.IOException; import java.io.PrintWriter; import javax.ejb.EJB; import javax.servlet.*; import javax.servlet.http.*; public class ServletTest1 extends HttpServlet { @EJB UniqueLocalBusinessInterface lc; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { [...] lc.sayLocalHello(); [...] } |
Первое, на что нужно обратить внимание, — это то, что EJB-сервер внедряется в наш сервлет сервером приложений, так как одного только интерфейса бина достаточно для определения целевого EJB-компонента. В этом случае элемент beanInterface аннотации @EJB принимает значение по умолчанию, как описано в нашем предыдущем посте , то есть тип внедренного поля: UniqueLocalBusinessInterface . Поскольку в приложении, реализующем этот бизнес-интерфейс, есть только один EJB-компонент, в поле lc сервлета вставляется ссылка на экземпляр такого класса.
Второе, на что стоит обратить внимание, — это то, что мы безопасно внедряем EJB в поле сервлета, потому что EJB не имеет состояния . Так как сервлет по умолчанию не имеет состояния, вам не следует вводить ресурсы с сохранением состояния в свойства полей сервлета, иначе вы можете столкнуться с проблемами, связанными с параллелизмом. Если вам нужно использовать EJB с состоянием в сервлете, вы должны получить ссылку с помощью программного поиска JNDI, так как это гарантирует, что новый экземпляр будет возвращен для каждой операции поиска.
Давайте развернем и запустим наше приложение, и мы увидим, что сервлет внедряет свой целевой EJB и вызов метода в метод sayLocalHello () его бизнес-интерфейса выполняется правильно.
Если бы мы хотели внедрить ссылку на удаленный интерфейс, клиентский код не был бы затронут. Если вы попытаетесь изменить UniqueLocalBusinessInterface с @Local на @Remote , вы увидите, что сервлет не видит изменений и продолжает работать правильно.
Что произойдет, если более одного EJB реализуют один и тот же интерфейс?
Давайте предположим, что мы добавили еще один EJB-компонент в наш EJB-модуль в этом приложении, который реализует тот же интерфейс, что и предыдущий, UniqueLocalBusinessInterface . В этом случае, поскольку интерфейса компонента больше не достаточно для определения целевого компонента для внедрения, вам будет возвращена ошибка. Например, при развертывании такого приложения на сервере приложений WebLogic возникает следующая ошибка:
1
|
[08:46:25 PM] Caused by: weblogic.deployment.EnvironmentException: [J2EE:160199]Error resolving ejb-ref 'es.reacts.ServletTest1/lc1' from module 'WebTest0' of application 'EJBTestApp' . The ejb-ref does not have an ejb-link and the JNDI name of the target bean has not been specified. Attempts to automatically link the ejb-ref to its target bean failed because multiple EJBs in the application were found to implement the 'es.reacts.UniqueLocalBusinessInterface' interface. Please specify a qualified ejb-link for this ejb-ref to indicate which EJB is the target of this ejb-ref. |
Внедрение ссылки на конкретный экземпляр EJB
Чтобы решить проблему, возникшую в предыдущем разделе, нам нужно предоставить серверу приложений необходимую информацию для определения целевого EJB. Как объяснялось в нашем предыдущем посте , мы можем использовать следующие два метода:
- Либо мы используем элемент name аннотации @EJB (или соответствующий элемент <ejb-ref-name /> дескриптора развертывания), чтобы объявить ссылку EJB в частном пространстве имен приложения и связать ее с целевым компонентом, используя дескриптор развертывания.
- Или мы используем элемент beanName аннотации @EJB (или соответствующий элемент <ejb-link /> дескриптора развертывания), чтобы сделать это непосредственно в нашем коде.
Отображение EJB в личное пространство имен
Используя первый метод, мы получим следующий код в нашем сервлете:
1
2
|
@EJB (name = "ejb/bean-name" ) UniqueLocalBusinessInterface lc; |
и следующий элемент в дескрипторе развертывания (web.xml) нашего веб-модуля Java EE, который действует как клиент EJB:
1
2
3
4
5
6
|
< ejb-local-ref > < ejb-ref-name >ejb/bean-name</ ejb-ref-name > < ejb-ref-type >Session</ ejb-ref-type > < local >es.reacts.UniqueLocalBusinessInterface</ local > < ejb-link >UniqueLocalSessionEJB</ ejb-link > </ ejb-local-ref > |
Элемент <ejb-link /> содержит имя компонента, которое мы определили в начале нашего примера с аннотацией:
1
|
@Stateless (name = "UniqueLocalSessionEJB" ) |
в классе реализации EJB.
Обратите внимание, что в этом примере мы явно использовали элемент имени @EJB, но мы могли бы установить ссылку, используя ее значение по умолчанию. Значение по умолчанию для элемента name :
[квалифицированное имя класса] / [имя свойства или поля]
что в этом случае будет:
es.reacts.ServletTest1 / ЖХ
Недостаток использования автоматически сгенерированного имени по умолчанию вместе с EJB-связью с использованием <ejb-link /> заключается в том, что каждый раз, когда вы реорганизуете код, вам придется проверять дескрипторы развертывания. Хотя разработчики иногда думают иначе, Спецификация Java EE определяет некоторые другие роли, такие как ассамблер и развертыватель. В больших корпоративных средах такие профили нередко переопределяют аннотации разработчиков, чтобы «подключить» компоненты, используемые приложениями. Переопределение аннотации является одной из причин, по которой стандартные дескрипторы развертывания все еще существуют. Это особенно верно, когда ссылки на удаленные компоненты, внутри или снаружи приложения. По этой причине я предлагаю вам не полагаться на автоматически сгенерированные имена и использовать вместо них собственные хорошо документированные имена.
Связывание EJB со ссылкой в приватном пространстве имен
Второй метод обеспечивает прямой способ связать ссылку с ее целевым компонентом, используя элемент beanName аннотации @EJB . Код сервлета будет использовать следующую ссылку EJB:
1
2
|
@EJB (beanName = "UniqueLocalSessionEJB" ) UniqueLocalBusinessInterface lc; |
и нам не нужна дополнительная информация в дескрипторе развертывания.
Хотя этот метод позволяет разработчику связывать ссылку на EJB, не полагаясь на дескриптор развертывания, предложение, данное в конце предыдущего раздела, все еще действует. Помните, что аннотации могут быть переопределены во время развертывания! Не связывайте EJB со ссылкой, если вы заранее знаете, что такая ссылка может быть отменена. В таких случаях предпочтительнее назначить имя для ссылки, как описано в предыдущем разделе.
Ссылка: основные ссылки на EJB, инъекция и поиск от нашего партнера JCG Грея в блоге The Grey .