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

Я хочу создать источник, который позволяет внедрить 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 не Serializable и, следовательно, не может быть создан таким образом. При попытке получить доступ к JSF-странице, которая вызывает компонент, использующий мой ResourceBundle, я получаю следующую ошибку от Weld:

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 работать? Или есть какие-то другие механизмы, чтобы получить подобную функциональность? Заранее спасибо!

РЕДАКТИРОВАТЬ:

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

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

2 ответа

Решение

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

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

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

private transient @Inject ResourceBundle bundle;

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

private ResourceBundle bundle;

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

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

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

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

Начиная с 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 {
}

Затем в bean-компоненте с поддержкой пассивации введите следующее:

@Inject
@LogbackLogger
private Logger logger;
Другие вопросы по тегам