使用 CDI 而不是 @ManagedBean: UnproxyableResolutionException 因为超类没有无参数构造函数

Posted

技术标签:

【中文标题】使用 CDI 而不是 @ManagedBean: UnproxyableResolutionException 因为超类没有无参数构造函数【英文标题】:Using CDI instead of @ManagedBean: UnproxyableResolutionException because super class has no no-args constructor 【发布时间】:2011-04-19 21:47:12 【问题描述】:

我正在尝试将 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 异常:

严重:加载时出现异常 应用程序:WELD-001435 普通范围 bean 班级 com.web.AbstractCrudController 不可代理,因为它没有 无参数构造函数。 org.jboss.weld.exceptions.UnproxyableResolutionException: WELD-001435 普通范围的 bean 类 com.web.AbstractCrudController 不可代理,因为它没有 无参数构造函数。 在 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) 在 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) 在 com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1221) 在 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) 在 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) 在 com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:220) 在 com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135) 在 com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102) 在 com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88) 在 com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76) 在 com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53) 在 com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57) 在 com.sun.grizzly.ContextTask.run(ContextTask.java:69) 在 com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:530) 在 com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:511) 在 java.lang.Thread.run(Thread.java:637)

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

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

【问题讨论】:

【参考方案1】:

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

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

    通过从方法和类本身中删除 final 关键字 用@Singleton@Dependent 伪范围标记这样的“不可代理类”(当然,如果有意义的话)。它会起作用,因为 CDI 不会为伪作用域 bean 创建代理对象。

在 OP 用例中,建议使用第二种方法恕我直言,因为控制器绝对可以标记为单例。

希望对某人有所帮助

【讨论】:

【参考方案2】:

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

具有@Default 限定符的 CDI bean 祖先:

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

祖先 bean 有以下内容:

@PostConstruct
protected void init() 

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

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

后代豆有以下几点:

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

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

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

【讨论】:

【参考方案3】:

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

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

CDI 规范是这样写的:

5.4.1。不可代理的 bean 类型

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

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

如果一个注入点的声明 类型不能被代理 容器解析为一个带有 正常范围,容器 自动检测问题并 将其视为部署问题。

我的建议是使方法成为非最终方法。

参考文献

CDI 规范 第 5.4 节。 “客户代理” 第 5.4.1 节“不可代理的 bean 类型” 第 6.3 节。 "普通作用域和伪作用域"

【讨论】:

CDI 不使用代理的情况有哪些?我认为它应该总是:) (+1) @Bozho 何时使用伪作用域? a,是的,规范说“适用于所有正常范围” 谢谢。 @Bozho 规范说 CDI 不对具有 @Dependent 范围(这是默认范围)的 bean 使用客户端代理。 CDI 不会为所有伪作用域 bean 创建代理对象。所以对于@Dependent@Singleton。我真的很欣赏这个功能。

以上是关于使用 CDI 而不是 @ManagedBean: UnproxyableResolutionException 因为超类没有无参数构造函数的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JSF 2.0/2.1 中用 CDI 替换 @ManagedBean / @ViewScope

具有 CDI 注入属性的 ViewScoped ManagedBean 上的 NotSerializableException

如何配置启动托管bean?

Java EE 6:切换到 CDI - 最佳资源

JSF-2.3 找不到我的 @Named CDI-1.2 托管 bean

我如何使用@ConversationScoped