Usando o CDI em vez de @ManagedBean: UnproxyableResolutionException porque a Super Class não tem construtor de não-Args
-
27-09-2019 - |
Pergunta
Estou tentando usar o CDI para o meu aplicativo JSF/Java EE. Eu tenho a seguinte hierarquia de classe:
/**
* base controller class
* also contains some final methods and an inner enum class declaration
*/
public abstract class AbstractCrudController<K, E> implements Serializable {
private Class<E> entityClass;
public AbstractCrudController(Class<E> entityClass) {
this.entityClass = entityClass;
}
// ...
}
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
@Named
@SessionScoped
public class CategoryController extends AbstractCrudController<Long, Category> implements Serializable {
public CategoryController() {
super(Category.class);
}
//...
}
Quando tento implantar o aplicativo no GF 3.1, recebo a seguinte exceção CDI/solda:
Severo: exceção ao carregar o aplicativo: Weld-001435 Classe de feijão com escopo normal com.web.abstractcrudcontroller não é procurável porque não possui construtor de não-Args. org.jboss.weld.exceptions.unproxyableResolutionException: Weld-001435 Classe de feijão com escopo normal com.web.abstractcrudcontroller não é procurável porque não possui o construtor não-Args. Org.jboss.weld.util.proxies.getUnProxyableClassexception (proxies.java:215) em org.jboss.weld.util.proxies.getUnProxyableTypeexception (proxies.java:166) em org.jboss. (Proxies.java:191) em org.jboss.weld.bootstrap.validator.validateBean (validator.java:134) em org.jboss.weld.bootstrap.validator.validateribean (validator.java:148) em org.jboss. weld.bootstrap.Validator.validateBeans(Validator.java:363) at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:349) at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java: 416) em org.glassfish.weld.welddeployer.event (WeldDeployer.java:178) em org.glassfish.kernel.event.eventsimpl.send (eventsimplic.java:128) em org.glassfish.innstall.datafina.AppLination (ApplicationInfo.java:265) em com.sun.enterprise.v3.server.applicationlifecycle.deploy (ApplicationlifeCycle.java:402) em com.sun.enterprise.v3.server.ApplicationLifecycle.deplo org.glassfish.deployment.admin.deployCommand.execute (implantCommand.java:351) em com.sun.enterprise.v3.admin.commandrunnerimpl $ 1.execute (commandrunnerimpl.java:360) em com.sun.erMerPrise (com comandrunnerimpl.java:360) em com.sun.erMerPrise ( .CommandRunnerImpl.doCommand(CommandRunnerImpl.java:375) at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1072) at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1200(CommandRunnerImpl. java: 101) em com.sun.enterprise.v3.admin.Commandrunnerimpl $ ExecutionContext.execute (commandRunnerimpl.java:1221) em com.sun.enterprise.v3.admin.commandrunnerImpl $ ExecutionConxt.extenxt.exection.v3.admin.ComMRunnerImpl $ em com.sun.enterprise.v3.admin.adminadapter.docommand (adminadapter.java:375) em com.sun.enterprise.v3.admin.adminadapter.service (adminadapter.java:209) em com.sun.gra. .http11.grizzlyadapter.service (grizzlyadapter.java:166) em com.sun.enterprise.v3.server.hk2dispatcher.dispath (hk2dispatcher.java:117) AT.SUNEN.ENTERSERSERPRISE.VERVIDES.IMPLES.IMPERTIDES.IMPRICES.IMPRICES.JAVA: rvice (contingermapper.java:234) em com.sun.grizzly.http.processortsork.invokeadapter (processortosk.java:824) em com.sun.grizzly.http.processortosk.doprocess (processortosk.java:721) .Grizzly.http.processorTask.process (processortosk.java:1014) em com.sun.grizzly.http.defaultprotocolfilter.execute (defaultProtocolfilter.java:220) em com.sun.grizzly.defaultocolterColter.java:220) em com.sun.grizzly.defaultColcolter. ) at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102) at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88) at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain. java:76) at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53) at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57) at com.sun.grizzly.ContextTask.run(ContextTask .Java: 69) em com.sun.grizzly.util.abstractThreadpool $ worker.dowork (abstracthreadpool.java:530) em com.sun.grizzly.util.abst ractThreadpool $ worker.run (abstracthreadpool.java:511) em java.lang.thread.run (thread.java:637)
Mesmo se eu adicionar um construtor sem args à classe base, a Weld ainda reclama com a mesma exceção de que a classe não é procurável porque possui métodos finais. Por que a solda me força a mudar meu design de classe? Tudo funcionou bem usando a anotação JSF @ManagedBean.
Eu apreciaria qualquer ajuda. Obrigado, Theo
Solução
Por que a solda me força a mudar meu design de classe? Tudo funcionou bem usando a anotação JSF @ManagedBean.
Bem, Weld/CDI não funciona da mesma maneira. Meu entendimento é que, quando você usa injeção para obter uma referência a um feijão, o que você recebe é a maioria dos casos um objeto de proxy. Esse objeto de proxy submasca seu feijão e substitui os métodos para implementar a delegação. E isso introduz algumas restrições nas classes que o CDI pode proxy.
A especificação CDI coloca assim:
5.4.1. Tipos de feijão desprezíveis
Certos tipos de feijão legal não podem ser procurados pelo contêiner:
- classes que não têm um construtor não privado sem parâmetros,
- classes que são declaradas finais ou têm métodos finais,
- tipos primitivos,
- e tipos de matriz.
Se um ponto de injeção cujo tipo declarado não puder ser proxado pelo contêiner resolver um feijão com um escopo normal, o contêiner detecta automaticamente o problema e o trata como um problema de implantação.
Minha sugestão seria tornar os métodos não finais.
Referências
- Especificação CDI
- Seção 5.4. "Proxies de clientes"
- Seção 5.4.1 "Tipos de feijão insuficientes"
- Seção 6.3. "Escopos e pseudo-escopos normais"
Outras dicas
Estou em processo de migração do JSF gerenciado com feijões gerenciados pelo CDI, e acabei de confirmar que sou capaz de usar super Com sucesso em um CDI Bean descendente (com qualificador 'personalizado' @Descender) que 'estende' um feijão CDI ancestral (com o qualificador @default).
CDI Bean Ancestor com @Default Qualifier:
@Default
@Named("pf_pointOfContactController")
@SessionScoped
public class pf_PointOfContactController implements Serializable {
Ancestral Bean tem o seguinte:
@PostConstruct
protected void init() {
CDI Bean Descendente com @Descender Qualifier:
@Descendant
@Named("pf_orderCustomerPointOfContactController")
@SessionScoped
public class pf_OrderCustomerPointOfContactController extends pf_PointOfContactController {
Descendente Bean tem o seguinte:
@PostConstruct
public void init(){
super.init();
Eu tive que adicionar/usar super.init (), porque os métodos no Ancestor CDI Bean estavam levantando o NullPointerException, uma vez que o @PostConstruct do Ancestor Bean não é executado no CDI @Descender Bean.
Eu vi/ouvi/li que é recomendável usar @postconstruct em vez do método construtor ao usar o CDI, para que o construtor do ancestral Bean tenha a lógica de 'inicialização', e o construtor do Ancestor Bean foi automaticamente chamado/executado ao usar feijões gerenciados por JSF.
Como a resposta aceita é correta, mas incompleta, acho que posso adicionar meus dois centavos apenas para futuros leitores.
O problema que o OP encontrou pode ser resolvido de duas maneiras:
- Removendo
final
palavra -chave dos métodos e a classe em si - Marcando essa "classe não -fornecida" com
@Singleton
ou@Dependent
pseudo-escopo (é claro se faz sentido). Ele funcionará porque o CDI não cria um objeto proxy para feijões pseudo-escassos.
No caso de uso do OP, a segunda abordagem IMHO foi recomendada, pois os controladores definitivamente podem ser marcados como singletons.
Espero que ajude alguém