使用 CDI 而不是 @ManagedBean:UnproxyableResolutionException,因为超类没有无参数构造函数
我正在尝试将 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
嗯,Weld/CDI 的工作方式不同。我的理解是,当您使用注入来获取对 bean 的引用时,您在大多数情况中获得的是代理对象。该代理对象对您的 bean 进行子类化并重写实现委托的方法。这对 CDI 可以代理的类引入了一些限制。
CDI 规范是这样描述的:
我的建议是使这些方法成为非最终的。
参考
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:
My suggestion would be to make the methods non final.
References
我正在从 JSF 托管 bean 迁移到 CDI 托管 bean,并且我刚刚确认我能够使用 在后代 CDI bean(带有“自定义”@Desendant 限定符)中超级成功地“扩展”了祖先 CDI bean(带有 @Default 限定符)。
具有 @Default 限定符的 CDI bean 祖先:
祖先 bean 具有以下内容:
具有 @Descendant 限定符的 CDI bean 后代:
后代 bean 具有以下内容:
我必须添加/使用 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:
Ancestor bean has the following:
CDI bean descendant with @Descendant qualifier:
Descendant bean has the following:
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.
因为接受的答案是正确的但不完整,我想我可以为未来的读者添加我的两分钱。
OP遇到的问题可以通过两种方式解决:
final
关键字@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:
final
keyword from methods and class itself@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