Как внедрить несериализуемый класс (например, 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;