Como injetar uma classe não serializável (como java.util.ResourceBundle) com o Weld
-
25-09-2019 - |
Pergunta
Quero criar um Produtor que possibilite injetar um java.util.ResourceBundle em qualquer classe para obter Strings localizadas facilmente.Meu ResourceBundle-Producer se parece com isto:
public class ResourceBundleProducer {
@Inject
public Locale locale;
@Inject
public FacesContext facesContext;
@Produces
public ResourceBundle getResourceBundle() {
return ResourceBundle.getBundle("/messages", locale )
}
}
A injeção de Locale e FacesContext funciona (retirou os produtores correspondentes do Seam 3 Alpha Source).Mas, infelizmente, ResourceBundle não é serializável e, portanto, não pode ser produzido dessa forma.Estou recebendo o seguinte erro do Weld ao tentar acessar uma página JSF que chama um bean que usa meu 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
Existe alguma maneira de fazer meu ResourceBundleResolver funcionar?Ou existem outros mecanismos para obter uma funcionalidade semelhante?Desde já, obrigado!
EDITAR:
Ok, gastarei alguns dos meus pontos dificilmente conquistados;) também aceitará uma boa solução alternativa para este problema!
Tenho outro exemplo em que criar um Produtor não funciona:um FlashProdutor.Um FacesContext-Flash também não pode ser produzido porque o Flash não é serializável.
Solução
Bem, antes de tudo o ResourceBundle não é serializável. Ver aqui. E a mensagem é clara
não pode produzir não serializável Instâncias de injeção em não-transitório campos de feijão passivador
feijão passivador ??? Eu acho que Web.Pernsonhome é um feijão de sessão com estado ou um feijão @ConversationScoped. Estou certo ??? Se sim, você deveria Marque sua propriedade de pacote como transitória
private transient @Inject ResourceBundle bundle;
Outras dicas
De acordo com o tópico de comentários na resposta aceita de Arthur.eu segui este blog assim como Este para realizar um experimento de passivação/ativação.O experimento provou que o MrD comentou que a propriedade transitória será NULA na ativação.Assim, para lidar com propriedades de membros não serializáveis de um bean com capacidade de passivação (ou seja, beans de sessão com escopo de sessão, escopo de conversação e estado), sugiro a seguinte solução:
private ResourceBundle bundle;
@PostConstruct
@PostActivate
public void getResourceBundle() {
bundle = ResourceBundle.getBundle("/messages", locale );
}
Esta solução garante que os membros da propriedade não serializável sejam reinicializados sempre que entrarem no estado READY.
Uma última questão a abordar
Um último problema a ser resolvido é a injeção de um registrador SLF4j que não era serializável antes do slf4j 1.5.3, e Eu cito:
A partir do SLF4J versão 1.5.3, as instâncias do logger sobrevivem à serialização.Assim, a serialização da classe host não requer mais nenhuma ação especial, mesmo quando os madeireiros são declarados como variáveis de instância.
Assim, desde que sua dependência slf4j seja 1.5.3 ou posterior, você pode injetar com segurança um registrador SLF4j da seguinte maneira:
@Produces
@LogbackLogger
public Logger produceLogger(InjectionPoint injectionPoint){
return LoggerFactory.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
Supondo que você declarou o qualificador:
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface LogbackLogger {
}
Em seguida, em um feijão com capacidade de passivação, injete da seguinte forma:
@Inject
@LogbackLogger
private Logger logger;