使用CDI代替@ManagedBean:UnproxyableResolutionException,因为超类没有no-args构造函数

时间:2010-10-01 14:44:06

标签: jsf-2 cdi jboss-weld

我正在尝试将CDI用于我的JSF / Java EE应用程序。我有以下类层次结构:

/**
 * 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);
  }
  //...
}

当我尝试在GF 3.1上部署应用程序时,我得到以下CDI / Weld异常:

  

严重:加载时出现异常   app:WELD-001435正常的范围豆   类   com.web.AbstractCrudController   是不可代理的,因为它没有   no-args构造函数。   org.jboss.weld.exceptions.UnproxyableResolutionException:   WELD-001435正常范围的bean类   com.web.AbstractCrudController   是不可代理的,因为它没有   no-args构造函数。           在org.jboss.weld.util.Proxies.getUnproxyableClassException(Proxies.java:215)           在org.jboss.weld.util.Proxies.getUnproxyableTypeException(Proxies.java:166)           在org.jboss.weld.util.Proxies.getUnproxyableTypesException(Proxies.java:191)           在org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:134)           在org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:148)           在org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:363)           在org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:349)           在org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:416)           在org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:178)           在org.glassfish.kernel.event.EventsImpl.send(EventsImpl.java:128)           在org.glassfish.internal.data.ApplicationInfo.start(ApplicationInfo.java:265)           在com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:402)           在com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:221)           在org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:351)           at com.sun.enterprise.v3.admin.CommandRunnerImpl $ 1.execute(CommandRunnerImpl.java:360)           在com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:375)           在com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1072)           在com.sun.enterprise.v3.admin.CommandRunnerImpl.access $ 1200(CommandRunnerImpl.java:101)           at com.sun.enterprise.v3.admin.CommandRunnerImpl $ ExecutionContext.execute(CommandRunnerImpl.java:1221)           at com.sun.enterprise.v3.admin.CommandRunnerImpl $ ExecutionContext.execute(CommandRunnerImpl.java:1210)           在com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:375)           在com.sun.enterprise.v3.admin.AdminAdapter.service(AdminAdapter.java:209)           在com.sun.grizzly.tcp.http11.GrizzlyAdapter.service(GrizzlyAdapter.java:166)           在com.sun.enterprise.v3.server.HK2Dispatcher.dispath(HK2Dispatcher.java:117)           at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:234)           在com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:824)           在com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:721)           在com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1014)           at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:220)           at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135)           在com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102)           at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88)           在com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)           at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)           在com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)           在com.sun.grizzly.ContextTask.run(ContextTask.java:69)           at com.sun.grizzly.util.AbstractThreadPool $ Worker.doWork(AbstractThreadPool.java:530)           at com.sun.grizzly.util.AbstractThreadPool $ Worker.run(AbstractThreadPool.java:511)           在java.lang.Thread.run(Thread.java:637)

即使我向基类添加了一个no-args构造函数,Weld仍然会抱怨该类不可代理,因为它有最终方法。为什么WELD强迫我改变我的班级设计?使用JSF @ManagedBean批注一切正常。

我将不胜感激任何帮助。 谢谢, 西奥

3 个答案:

答案 0 :(得分:16)

  

为什么WELD强迫我改变班级设计?使用JSF @ManagedBean注释,一切正常。

好吧,Weld / CDI的工作方式不同。我的理解是,当您使用注入来获取对bean的引用时,您获得的是大多数情况下代理对象。此代理对象对您的bean进行子类化,并覆盖实现委派的方法。这引入了CDI可以代理的类的一些限制。

CDI规范如下:

  

5.4.1。不可提供的bean类型

     

某些合法的bean类型不能   由容器代理:

     
      
  • 没有非私有构造函数但没有的类   参数,
  •   
  • 声明为final或具有最终方法的类
  •   
  • 原始类型,
  •   
  • 和数组类型。
  •   
     

如果注入点已声明   类型不能被代理   容器用a解析为bean   正常范围,容器   自动检测问题和   将其视为部署问题。

我的建议是让这些方法不是最终的。

参考

  • CDI规范
    • 第5.4节。 “客户代理”
    • 第5.4.1节“不可提供的bean类型”
    • 第6.3节。 “正常范围和伪范围”

答案 1 :(得分:0)

我正在从JSF托管bean迁移到CDI托管bean,我刚刚确认我能够在后代CDI bean中成功使用super(使用'custom'@Descendant限定符) '扩展'祖先CDI bean(使用@Default限定符)。

使用@Default限定符的CDI bean祖先:

@Default
@Named("pf_pointOfContactController")
@SessionScoped
public class pf_PointOfContactController implements Serializable {

Ancestor bean有以下内容:

@PostConstruct
protected void init() {

带有@Descendant限定符的CDI bean后代:

@Descendant
@Named("pf_orderCustomerPointOfContactController")
@SessionScoped
public class pf_OrderCustomerPointOfContactController extends pf_PointOfContactController {

后代bean有以下内容:

@PostConstruct
public void init(){
    super.init();

我必须添加/使用super.init(),因为祖先CDI bean中的方法引发了NullPointerException,因为祖先bean的@PostConstruct没有在CDI @Descendant bean中执行。

我已经看到/听到/读过在使用CDI时建议使用@PostConstruct而不是Constructor方法,因此祖先bean的构造函数具有'初始化'逻辑,并且在使用JSF时自动调用/执行祖先bean的构造函数托管豆。

答案 2 :(得分:0)

因为接受的答案是正确但不完整的我认为我可以为未来的读者添加我的两分钱。

OP遇到的问题可以通过两种方式解决:

  1. 从方法和类本身中删除final关键字
  2. 标记此类&#34;不可提及的类&#34;使用@Singleton@Dependent伪范围(当然,如果有意义的话)。它将起作用,因为CDI不会为伪作用域bean创建代理对象。
  3. 在OP用例中推荐使用第二种方法IMHO,因为控制器肯定可以标记为单例。

    希望它有助于某人