Using CDI instead of @ManagedBean: UnproxyableResolutionException because super class has no no-args constructor

ε祈祈猫儿з 提交于 2019-12-05 05:12:20

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"

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.

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!