Как внедрить несериализуемый класс (например, java.util.ResourceBundle) с помощью Weld

Я хочу создать Producer, который позволит внедрить java.util.ResourceBundle в любой класс, чтобы легко получить локализованные строки. Мой ResourceBundle-Producer выглядит так:

public class ResourceBundleProducer {
  @Inject       
  public Locale locale;

  @Inject       
  public FacesContext facesContext;

  @Produces
  public ResourceBundle getResourceBundle() {
    return ResourceBundle.getBundle("/messages", locale )
  }
}

Работает Injection of Locale и FacesContext (взял соответствующих производителей из Seam 3 Alpha Source). Но, к сожалению, ResourceBundle не сериализуем и, следовательно, не может быть создан таким образом. Я получаю следующую ошибку от Weld при попытке доступа к JSF-странице, которая вызывает bean-компонент, использующий мой ResourceBundle:

Caused by: org.jboss.weld.IllegalProductException: WELD-000054 Producers cannot produce non-serializable instances for injection into non-transient fields of passivating beans\\n\\nProducer\: org.jboss.weld.bean-/D:/Program Files (x86)/GlassFish-Tools-Bundle-For-Eclipse-1.2/glassfishv3/glassfish/domains/teachernews/applications/teachernews/-ProducerMethod-services.producers.ResourceBundleProducer.getResourceBundle()\\nInjection Point\: field web.PersonHome.bundle

Есть ли способы заставить мой ResourceBundleResolver работать? Или есть какие-то другие механизмы для получения аналогичного функционала? Заранее спасибо!

ИЗМЕНИТЬ:

Хорошо, я потрачу часть своих с трудом заработанных баллов ;) Также приму хороший обходной путь для этой проблемы!

У меня есть еще один пример, когда создание Producer не работает: FlashProducer. FacesContext-Flash также не может быть создан, потому что Flash не сериализуем.


person Wolkenarchitekt    schedule 08.06.2010    source источник


Ответы (2)


Ну, во-первых, ResourceBundle не сериализуем. См. здесь. И сообщение ясно

не может создавать несериализуемые экземпляры для внедрения в непереходные поля пассивирующих компонентов

пассивация компонентов ??? Я думаю, что web.PersonHome — это либо сеансовый компонент с отслеживанием состояния, либо компонент @ConversationScoped. Я прав ??? Если это так, вам следует пометить свойство пакета как временное.

private transient @Inject ResourceBundle bundle;
person Arthur Ronald    schedule 17.07.2010
comment
Отказ от ответственности @ifischer: я еще не использую Weld. Я использую Шов. Поэтому я не уверен, можно ли использовать аннотацию @Inject вместе с переходным - person Arthur Ronald; 17.07.2010
comment
Поздравляю, вы только что заработали себе 50 баллов ;) Наконец-то решение! Интересно, почему это заняло так много времени + Щедрость, ведь это довольно просто (также интересно, почему я не узнал сам...). Только что проверил, также работает с CDI/Weld. Теперь я наконец-то могу внедрить Resourcebundles и Contexts. - person Wolkenarchitekt; 17.07.2010
comment
Кстати, PersonHome — это CDI/Weld-Bean @ConversationScoped (@Named) - person Wolkenarchitekt; 18.07.2010
comment
@ifischer Вы правы. Поскольку ConversationScoped должен быть сериализуемым между запросами, все его поля должны реализовывать интерфейс Serializable, если вы не пометите его как временный. Это объясняет, почему вы получаете сообщение об ошибке. Та же проблема возникает с компонентами Stateful Session. - person Arthur Ronald; 18.07.2010
comment
Но если bundle помечен как transient, как вы можете быть уверены, что он доступен, когда вам это нужно? В случае пассивации/активации компонента ResourceBundle исчезнет, ​​и доступ к нему вызовет исключение. - person MrD; 06.11.2013
comment
@MrD Через секунду, однако, ResourceBundle никогда не может оставаться нулевым, поскольку сервер приложений повторно вставит его заново, как только будет определено как нулевой. Это базовая функциональность любого контейнера, реализующего IoC. - person Sym-Sym; 05.01.2014
comment
@Sam, но CDI-инъекция выполняется только один раз во время создания компонента. Таким образом, контейнер (т.е. Weld) не будет перепроверять значения своих полей после пассивации/активации. Доступ к полю после пассивации/активации приведет к исключению NullPointerException. - person MrD; 07.01.2014
comment
@MrD, пожалуйста, ознакомьтесь с ответом, который я добавил - person Sym-Sym; 05.02.2014
comment
transient исправляет исключение, но все же я получаю предупреждающий треугольник о неудовлетворенных зависимостях. Мой beans.xml имеет bean-discovery-mode="all" в обоих проектах (где @Cached аннотация и класс производителя и где @Cached является @Inject), но все же с обеих сторон я получаю это, как и для @Inject private CacheManager manager;. Любые идеи? - person Roland; 02.09.2017
comment
В чем разница между private transient @Inject и @Inject private transient? Использование первого устранило ошибку IntelliJ. - person Panu Haaramo; 14.03.2020

Согласно ветке комментариев в принятом ответе Артура. Я подписался на этот блог, а также этот для проведения эксперимента по пассивации/активации. Эксперимент подтвердил комментарий MrD о том, что при активации переходное свойство будет иметь значение NULL. Таким образом, чтобы иметь дело с несериализуемыми свойствами-членами компонента с возможностью пассивации (а именно, сеансовых компонентов с областью сеанса, диалоговой области и сеансовых компонентов с состоянием), я предлагаю следующее решение:

private ResourceBundle bundle;

@PostConstruct
@PostActivate
public void getResourceBundle() {
    bundle = ResourceBundle.getBundle("/messages", locale );
}

Это решение гарантирует повторную инициализацию несериализуемых членов свойства каждый раз, когда оно переходит в состояние READY.

Последний вопрос для решения

Последняя проблема, которую необходимо решить, — это внедрение регистратора SLF4j, который был несериализуем до версии slf4j 1.5.3, и I цитата:

Начиная с SLF4J версии 1.5.3, экземпляры регистратора выдерживают сериализацию. Таким образом, сериализация хост-класса больше не требует каких-либо специальных действий, даже если регистраторы объявлены как переменные экземпляра.

Таким образом, пока ваша зависимость slf4j 1.5.3 или более поздняя, ​​вы можете безопасно внедрить регистратор SLF4j следующим образом:

@Produces
@LogbackLogger
public Logger produceLogger(InjectionPoint injectionPoint){
    return LoggerFactory.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}

Предполагая, что вы объявили квалификатор:

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface LogbackLogger {
}

Затем в бин, способный к пассивации, введите следующее:

@Inject
@LogbackLogger
private Logger logger;
person Sym-Sym    schedule 04.02.2014
comment
Что касается производителя для ResourceBundle: если я правильно понимаю, вы не больше используете производителя для ResourceBundle - теряете очарование CDI. Вместо этого вы получаете ResourceBundle вручную для событий PostConstruct и -Activate (кстати: методы PostConstruct и -Activate НЕ ДОЛЖНЫ возвращать значение). Так что это полностью решение, отличное от CDI, которое можно было бы использовать и в JEE5, не так ли? - person MrD; 05.02.2014
comment
@MrD Совершенно верно! - person Sym-Sym; 05.02.2014
comment
@MrD Я отредактировал метод инициализации, чтобы он возвращал void. Спасибо за ваш острый глаз. - person Sym-Sym; 05.02.2014
comment
Я предполагаю, что аннотацию @PostConstruct следует удалить, чтобы вы все еще могли использовать CDI. - person Armando; 24.05.2015
comment
@ Армандо, мне нравится твое предложение. Хотя я не пробовал... Интересно, что скажет об этом MrD, он был прав с самого начала. - person Sym-Sym; 27.05.2015