使用 CDI 代替 @ManagedBean:UnproxyableResolutionException 因为超类没有无参数构造函数
-
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遇到的问题可以通过两种方式解决:
- 通过删除
final
方法和类本身的关键字 - 将此类“不可代理的类”标记为
@Singleton
或者@Dependent
伪范围(当然,如果它有意义的话)。它会起作用,因为 CDI 不会为伪作用域 bean 创建代理对象。
在OP用例中,建议使用第二种方法恕我直言,因为控制器绝对可以标记为单例。
希望它对某人有帮助