8

我正在尝试将 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 仍然会抱怨该类不可代理,因为它具有最终方法。为什么 WELD 强迫我改变我的班级设计?使用 JSF @ManagedBean 注释一切正常。

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

4

3 回答 3

16

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

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

CDI 规范是这样描述的:

5.4.1。不可代理的 bean 类型

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

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

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

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

参考

  • CDI 规范
    • 第 5.4 节。“客户代理”
    • 第 5.4.1 节“不可代理的 bean 类型”
    • 第 6.3 节。“普通作用域和伪作用域”
于 2010-10-01T15:28:24.487 回答
0

我正在从 JSF 托管 bean 迁移到 CDI 托管 bean,我刚刚确认我能够在“扩展”祖先 CDI bean 的后代 CDI bean(带有“自定义”@Descendant 限定符)中成功使用super(使用 @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 {

后代bean具有以下内容:

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

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

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

于 2012-11-21T09:09:49.950 回答
0

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

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

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

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

希望它可以帮助某人

于 2015-11-11T19:27:55.477 回答