I'm working on a JSF project using CDI. We are using SonarQube to manage the code quality. One of the issues that popped up after running a scan on our project is S3306: "Constructor injection should be used instead of field injection".
It was triggered by injects we are using in our beans, for example:
@Named
@ViewScoped
public class AccountsController extends AbstractController<Account> {
@Inject
private AccountsFacade accountsFacade;
public AccountsController() {
super(Account.class);
}
...
}
Injected are facades like:
@Stateless
public class AccountsFacade extends AbstractFacade<Account> {
@PersistenceContext(unitName = "...")
private EntityManager entityManager;
public AccountsFacade() {
super(Account.class);
}
...
}
The info on this issue provided by SonarQube:
Field injection seems like a tidy way to get your classes what they need to do their jobs, but it's really a
NullPointerException
waiting to happen unless all your class constructors areprivate
. That's because any class instances that are constructed by callers, rather than instantiated by the Spring framework, won't have the ability to perform the field injection.Instead
@Inject
should be moved to the constructor and the fields required as constructor parameters.This rule raises an issue when classes with non-
private
constructors (including the default constructor) use field injection.
The suggested solution:
class MyComponent {
private final MyCollaborator collaborator;
@Inject
public MyComponent(MyCollaborator collaborator) {
Assert.notNull(collaborator, "MyCollaborator must not be null!");
this.collaborator = collaborator;
}
public void myBusinessMethod() {
collaborator.doSomething();
}
}
Since managed beans are created using a constructor with no arguments, is there any way to comply with the SonarQube rule?
Edit: I think this is relevant: we are using CDI. I'm not sure if the previous link (Oracle documentation) applies here.
Edit 2: I just tried the suggested solution after I've read documentation on WELD injection points. But that gives me this error:
WELD-001435: Normal scoped bean class ...AccountsController is not proxyable because it has no no-args constructor
Edit 3: The error in edit 2 is indeed (see comments at the question) caused by the @Inject
of the AccountsController
in an other controller. See also the answer.