Hendrik Ebbers
Hendrik Ebbers

Reputation: 2600

CDI / WELD Constructor injection best practices

In a "monolitic" Jakarta-EE 8 application we want to use JSF in combination with CDI. The following graph gives an example on how the view and classes depend on each other:

JSF-View -> ViewController -> BeanA --> BeanA1
                                    \-> BeanA2
                                

In this case the ViewController is @Named + @ViewScoped and all other beans (BeanA, BeanA1, BeanA2) are @SessionScoped. We want to use constructor injection as a best practice. Based on this our classes looks like this:

@Named
@ViewScoped
public class ViewController implements Serializable {

    private final BeanA bean;
    
    @Inject
    public ViewController(final BeanA bean) {
        this.bean = bean;
    }
}

@SessionScoped
public class BeanA implements Serializable {

    private final BeanA1 bean1;
    
    private final BeanA2 bean2;
    
    @Inject
    public BeanA(final BeanA1 bean1, final BeanA2 bean2) {
        this.bean1 = bean1;
        this.bean2 = bean2;
    }
}

When deploying this as a WAR to Wildfly 20 we end with the following error / exception:

"BeanA is not proxyable because it has no no-args constructor".

Since we do not plan to run the server in a cluster I do not get why we need a non-args constructor (no serialization needed at all).

Adding the META-INF/org.jboss.weld.enableUnsafeProxies file solves the problem and we can deploy and run the application without any error. I ask myself if this is a good practice or if we miss anything?

Upvotes: 1

Views: 1058

Answers (1)

Laird Nelson
Laird Nelson

Reputation: 16238

First, the quickest possible answer: any bean that is in a normal scope must have a non-private, zero-argument constructor. In addition, such classes must not be final and must not have non-private, virtual final methods. @SessionScoped is a normal scope.

If you follow the link, you'll see that the reason for this in the CDI specification is not (perhaps primarily) because of serialization but because of proxying.

The property you refer to is a Weld-specific feature. If you know you are going to continue using Weld for your CDI implementation then you may of course continue to use this property, but strictly speaking your CDI application is now non-portable. This may or may not matter to you.

The most pragmatic, real-world solution to this problem that I've found is to define a package-private zero argument constructor that is @Deprecated that sets fields to null.

Upvotes: 6

Related Questions