使用 CDI 代替 @ManagedBean:UnproxyableResolutionException 因为超类没有无参数构造函数

StackOverflow https://stackoverflow.com/questions/3840240

  •  27-09-2019
  •  | 
  •  

我正在尝试将 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普通示波豆类com.web.web.abstractcrudcontroller不可临时,因为它没有no-args构造函数。org.jboss.weld.exceptions.UnproxyableResolutionException:WELD-001435普通示波豆类com.web.web.abstractcrudcontroller不可临时,因为它没有no-args构造函数。at org.jboss.weld.util.Proxies.getUnproxyableClassException(Proxies.java:215) at org.jboss.weld.util.Proxies.getUnproxyableTypeException(Proxies.java:166) at org.jboss.weld.util.Proxies.getUnproxyableTypesException (Proxies.java:191)在org.jboss.weld.weld.bootstrap.validator.validatebean(valiantor.java:134)at org.jboss.weld.weld.weld.bootstrap.validator.validater.validateribean(validator.java:148:148)at 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)at org.glassfish.weld.welddeployer.event(welddeployer.java:178)org.glassfish.kernel.kernel.kernel.kernel.events.events.events.eventsimpl.send(Eventsimpl.java:128) (applicationInfo.java:265)在com.sun.enterprise.v3.server.applicationlifecycle.deploy(applicationlifecycle.java:402)上,请org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:351) at com.sun.enterprise.v3.admin.CommandRunnerImpl$1.execute(CommandRunnerImpl.java:360) at com.sun.enterprise.v3.admin 。 Java:101)在com.sun.enterprise.v3.admin.commandrunnerimpl $ executionContext.execute(commnrrunnerimpl.java:1221)上在com.sun.enterprise.v3.admin.admin.adminadapter.docommand(AdminAdapter.java:375)上,com.sun.enterprise.v3.admin.adminadapter.service.service.service(adminadapter.java:209) .http11.grizzlyadapter.service(grizzlyadapter.java:166)at com.sun.enterprise.v3.server.hk2dispatcher.dispather.dispath(hk2dispatcher.java:117) (continerMapper.java:234)访问com.sun.grizzly.http.processortask.invokeadapter(processOrtask.java:824)actom.sun.grizzly.http.processortask.doprocess.doprocess(ProcessOrtask.java:721)在com.sun at Com.sun。 grizzly.http.processortask.process(ProcessOrtask.java:1014)ar.com.sun.grizzly.http.defaultprotocolfilter.execute(defaultProtocolfilter.java:220)在com.sun.grizzly.defaultprotocolchain.execute(defaultprotocolchain.java:102)com.sun.grizzly.defaultprotococolchain.execute(defaultprotococolchain.java:88)at Com.grizzly.sun.grizzly.httpp.httpppppppppprototcocolcoltecrotecolavecolicolatecrotecrotcocol(htppprotcolavecolaivecolaivecolaivecrotecrotecrotecro) :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)in com.sun.grizzly.util.util.util.util.util.threadpool $ worker.dowork(AbstractThreadPool.java:530)atom.sun.grizzlizzly.util.util.util.util.util.util.util.util.util.util.util.util.util.util.util.util.util.util.uthreadpool $ worker.run(acmpact threadpool.java:511)在java.lang.lang.lang.lang.lang.lang.lang.lang.lang.lang.lang.lang .thread.run(thread.java:637)

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

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

有帮助吗?

解决方案

为什么 WELD 强迫我改变我的类设计?使用 JSF @ManagedBean 注释一切工作正常。

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

CDI 规范是这样描述的:

5.4.1.不可代理的 Bean 类型

某些法定豆类不能 由容器代理:

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

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

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

参考

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

其他提示

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

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

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

祖豆有以下特点:

@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 时自动调用/执行祖先 bean 的构造函数。

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

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

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

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

希望它对某人有帮助

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top