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
NullPointerExceptionwaiting 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
@Injectshould be moved to the constructor and the fields required as constructor parameters.This rule raises an issue when classes with non-
privateconstructors (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.