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

发布于 2024-09-25 17:05:52 字数 4604 浏览 3 评论 0原文

我正在尝试将 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 普通作用域 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 注释一切正常。

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

I'm trying to use CDI for my JSF/Java EE application. I have the following class hierarchy:

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

When I try to deploy the application on GF 3.1, I get the following CDI/Weld exception:

SEVERE: Exception while loading the
app : WELD-001435 Normal scoped bean
class
com.web.AbstractCrudController
is not proxyable because it has no
no-args constructor.
org.jboss.weld.exceptions.UnproxyableResolutionException:
WELD-001435 Normal scoped bean class
com.web.AbstractCrudController
is not proxyable because it has no
no-args constructor.
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)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:134)
at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java: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)
at org.glassfish.kernel.event.EventsImpl.send(EventsImpl.java:128)
at org.glassfish.internal.data.ApplicationInfo.start(ApplicationInfo.java:265)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:402)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:221)
at 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.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)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1221)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1210)
at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:375)
at com.sun.enterprise.v3.admin.AdminAdapter.service(AdminAdapter.java:209)
at com.sun.grizzly.tcp.http11.GrizzlyAdapter.service(GrizzlyAdapter.java:166)
at com.sun.enterprise.v3.server.HK2Dispatcher.dispath(HK2Dispatcher.java:117)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:234)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:824)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:721)
at 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)
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)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:530)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:511)
at java.lang.Thread.run(Thread.java:637)

Even if I add a no-args constructor to the base class, Weld still complains with the same exception that the class is not proxyable because it has final methods. Why does WELD force me to change my class design? Everything worked fine using the JSF @ManagedBean annotation.

I would appreciate any help.
Thank,
Theo

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

扛刀软妹 2024-10-02 17:05:52

为什么 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 节。 “正常作用域和伪作用域”

Why does WELD force me to change my class design? Everything worked fine using the JSF @ManagedBean annotation.

Well, Weld/CDI doesn't work the same way. My understanding is that when you use injection to obtain a reference to a bean, what you get is in most cases a proxy object. This proxy object sub-classes your bean and overrides the methods to implement delegation. And this introduces some restrictions on the classes CDI can proxy.

The CDI spec puts it like this:

5.4.1. Unproxyable bean types

Certain legal bean types cannot be
proxied by the container:

  • classes which don't have a non-private constructor with no
    parameters,
  • classes which are declared final or have final methods,
  • primitive types,
  • and array types.

If an injection point whose declared
type cannot be proxied by the
container resolves to a bean with a
normal scope, the container
automatically detects the problem and
treats it as a deployment problem.

My suggestion would be to make the methods non final.

References

  • CDI Specification
    • Section 5.4. "Client proxies"
    • Section 5.4.1 "Unproxyable bean types"
    • Section 6.3. "Normal scopes and pseudo-scopes"
蓝眸 2024-10-02 17:05:52

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

后代 bean 具有以下内容:

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

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

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

I am in the process of migrating from JSF Managed beans to CDI managed beans, and I just confirmed that I am able to use super successfully in a descendant CDI bean (with 'custom' @Descendant qualifier) that 'extends' an ancestor CDI bean (with @Default qualifier).

CDI bean ancestor with @Default qualifier:

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

Ancestor bean has the following:

@PostConstruct
protected void init() {

CDI bean descendant with @Descendant qualifier:

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

Descendant bean has the following:

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

I had to add/use super.init(), because methods in the ancestor CDI bean were raising NullPointerException, since ancestor bean's @PostConstruct is not executed in CDI @Descendant bean.

I've seen/heard/read that it is recommended to use @PostConstruct instead of Constructor method when using CDI, so ancestor bean's constructor had the 'initialization' logic, and ancestor bean's constructor was automatically invoked/executed when using JSF managed beans.

独孤求败 2024-10-02 17:05:52

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

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

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

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

希望它对某人有帮助

Because accepted answer is correct but incomplete I think I can add my two cents just for future readers.

The problem which OP encountered can be solved in two ways:

  1. By removing final keyword from methods and class itself
  2. Marking such "unproxyable class" with @Singleton or @Dependent pseudo-scope (of course if it makes sense). It will work because CDI doesn't create a proxy object for pseudo-scoped beans.

In the OP use case the second approach IMHO was recommended, as controllers definitely can be marked as singletons.

Hope it helps somebody

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文