Как написать Java EE / EJB Синглтон?

день назад мое приложение было одним ухом, содержащим одну войну, одну банку EJB и пару файлов утилиты JAR. У меня был класс POJO singleton в одном из этих служебных файлов, он работал, и все было хорошо с миром:

EAR
 |--- WAR
 |--- EJB JAR
 |--- Util 1 JAR
 |--- Util 2 JAR
 |--- etc.

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

EAR
 |--- WAR 1
 |--- WAR 2
 |--- EJB JAR
 |--- Util 1 JAR
 |--- Util 2 JAR
 |--- etc.

Итак, я ищу способ чтобы создать объект Java singleton, который будет работать через войны (через загрузчики классов?). The @Singleton аннотация EJB казалась довольно многообещающей, пока я не обнаружил, что JBoss 5.1, похоже, не поддерживает эту аннотацию (которая была добавлена как часть EJB 3.1). Я что - то пропустил-могу ли я использовать @Singleton С JBoss 5.1? Обновление до JBoss как 6 не является вариантом прямо сейчас.

в качестве альтернативы, я был бы так же рад не использовать EJB для реализации моего синглтона. Что еще я могу сделать, чтобы решить эту проблему? В принципе, мне нужен полу-application-wide* hook в целую кучу других объектов, таких как различные кэшированные данные и информация о конфигурации приложения. В качестве последнего средства я уже рассматривал возможность объединения двух моих войн в одну, но это было бы довольно адски.

*значение: доступно в основном в любом месте над определенным слоем; на данный момент, в основном в моих войнах - вид и контроллер (в свободном смысле).

Edit: я действительно должен был назвать это Java EE а чем J2EE, не так ли?


Edit 2: большое спасибо еще раз @Yishai за всю помощь. После некоторых проб и ошибок похоже, что я понял, как использовать один загрузчик классов в войнах под JBoss 5. Я подробно описываю это ниже для себя, и, надеюсь, другие найдут это полезным.

N. B. Это довольно отличается от этого в JBoss 4 (см. ответ Ишая или мои ссылки ниже).

вместо написания jboss-web.xml для каждой войны и a jboss.xml для уха EJB-JAR, положить jboss-classloading.xml файл в каждой войне, в том же месте, что и DD (web.xml). Содержание jboss-classloading.xml должно быть:

<?xml version="1.0" encoding="UTF-8"?>
<classloading
    xmlns="urn:jboss:classloading:1.0"
    name="mywar.war"
    domain="DefaultDomain"
    parent-domain="Ignored"
    export-all="NON_EMPTY"
    import-all="true">
</classloading>

это следует из JBoss CW здесь, тогда как то, что (я думаю) работает для JBoss 4.X описана здесь. Более общая информация о JBoss classload (ing/ers):

насколько я могу судить, Вики-документы сообщества JBoss довольно отсутствуют для JBoss 5 по сравнению с JBoss 4.

5 ответов


хотя EJB3.1 spec вводит Синглтон, и ваша версия JBoss не поддерживает его, вы можете использовать аннотацию JBoss @Service для создания синглтона. Инструкции здесь. Кроме того, кажется, что у вас есть JBoss, настроенный для изоляции ваших банок ejb и войн друг от друга. Ты не должна этого делать. Вы можете посмотреть на загрузчика-репозиторий тег в конкретных xml-файлах JBoss, чтобы все ваше ухо делилось одним загрузчиком классов (или, возможно, по крайней мере, двумя войны разделяют один classloader).

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

идея наличия кэшированного состояния объекта сама по себе в порядке, особенно с EJB3, где вы можете введите свое состояние вместо статической ссылки на него (если вы используете аннотацию @Service, то вам нужна аннотация @Depends JBoss). При этом, если бы вы использовали "правильный" синглтон здесь, я бы ожидал, что ваша единственная проблема с тем, что у ваших войн есть два отдельных загрузчика классов, - это дополнительный объем памяти. В противном случае вы находитесь в проблемной области синглетов (где они должны быть инициализированы для использования, все, что их использует, должно гарантировать, что они сначала инициализируется, и, конечно, весь код сильно связан с их инициализацией).

где Синглеты действительно очень плохи, там они хранят состояние, так что один класс может изменить состояние, а другой класс поднимает его. Это в основном нет-нет в EJBs до 3.1, и даже тогда это делает много проблем параллелизма.

Edit (далее): Итак, вы хотите пойти с репозиторием classloader. Я использую JBoss 4.2.3, поэтому я не обязательно знаю все входы и выходы JBoss5 (который переписал свой загрузчик классов, хотя они говорят, что он почти полностью обратно совместим), однако в 4.2.x по умолчанию ваша конфигурация не вызывает проблем, потому что все уши, развернутые на сервере, используют один и тот же загрузчик классов ("unified classloader"). Я подозреваю, что сервер, на котором вы развертываете, имеет конфигурацию по-другому, поэтому я не уверен, как с ним взаимодействовать, но вам нужно добавить файл под названием jboss-app.xml в ухе (в том же месте как приложение.XML), который выглядит примерно так:

 <?xml version="1.0"?>
 <!DOCTYPE jboss-app PUBLIC "-//JBoss//DTD J2EE Application 4.2//EN"
        "http://www.jboss.org/j2ee/dtd/jboss-app_4_2.dtd">
 <jboss-app>
      <loader-repository>
      com.yourcomany:archive=yourear
      </loader-repository>
 </jboss-app>

Это для JBoss 4.2. 5.1 имеет тот же тип тега, вот это xsd-схемы. Он имеет ту же концепцию loader-repository.

что должны будет. То есть до тех пор, пока ваш ejb-jar, война и т. д. нет, тогда они не нужны. Однако ваши войны (в jboss-web.xml - то же расположение, что и в интернете.xml) может понадобиться то же самое. В этом случае, пока вы называете репозиторий точно так же (если я правильно понимаю - никогда не пробовал сам) они будут делиться одним и тем же загрузчиком классов. То же самое касается EJB, настроенного в jboss.xml, который находится в том же месте, что и ejb.XML.

этой может сделать это немного яснее.


Я бы настроил отдельный пул объектов на вашем сервере приложений, чтобы он содержал только один экземпляр.

Почему вы хотите это сделать, это реальный вопрос. Похоже, все ваши приложения будут соединены таким образом. И Google искореняет singleton из своих приложений. Почему ты считаешь нужным вернуть его?


вы можете использовать MBean и привязать его к JNDI, а затем получить его там, где вы хотите его использовать.

MBean может быть развернут в a .файл ОАР


Если вы используете Java EE 6, то он поддерживает одноэлементные EJBs.


Если это практично, просто возьмите класс, который имеет синглтон, поместите его в банку, выньте банку из уха и добавьте банку в загрузчик классов JBoss (через системный путь к классам или некоторый каталог lib). Это помещает класс в один загрузчик классов, общий для обеих войн.

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

однако, ничто не останавливает вас от впрыскивать в singleton (при запуске сервера) класс фабрики, который происходит от WARs et al, и этот класс имеет доступ ко всем классам приложений. Это делает singleton более простым контейнером.

но это просто сделать.

кроме того, если вы это сделаете, убедитесь, что при закрытии приложения все экземпляры, удерживаемые этим синглтоном, освобождаются. Любая ссылка на класс синглтоном не будет GC'D при развертывании приложения. Поэтому, если у вас есть ссылка для вашего приложения, хранящегося в одноэлементном элементе, сервер содержит ссылку на загрузчик классов singletons, который содержит ссылку на ваш одноэлементный класс, который содержит ссылку на ваш класс приложений, который содержит ссылку на загрузчик классов приложений и который содержит ссылку на все классы в вашем приложении. Не много, чтобы оставить позади.