如何在符合 SonarQube 规则 S3306 的 CDI 托管 bean 中使用 @Inject

How to use @Inject in CDI managed beans compliant with SonarQube rule S3306

我正在使用 CDI 开发一个 JSF 项目。我们使用 SonarQube 来管理代码质量。 运行 扫描我们的项目后弹出的问题之一是 S3306:“Constructor injection should be used instead of field injection”。

它是由我们在 bean 中使用的注入触发的,例如:

@Named
@ViewScoped
public class AccountsController extends AbstractController<Account> {

  @Inject
  private AccountsFacade accountsFacade;

  public AccountsController() {
    super(Account.class);
  }

  ...
}

注入的外观如下:

@Stateless
public class AccountsFacade extends AbstractFacade<Account> {

  @PersistenceContext(unitName = "...")
  private EntityManager entityManager;

  public AccountsFacade() {
    super(Account.class);
  }

  ...
}

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 are private. 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.

建议的解决方案:

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();
  }
}

managed beans are created using a constructor with no arguments以来,有什么方法可以遵守SonarQube规则吗?

编辑:我认为这是相关的:我们正在使用 CDI。我不确定之前的 link(Oracle 文档)是否适用于此。

编辑 2:我刚刚在阅读 WELD injection points 上的文档后尝试了建议的解决方案。但这给了我这个错误:

WELD-001435: Normal scoped bean class ...AccountsController is not proxyable because it has no no-args constructor

编辑 3:编辑 2 中的错误确实(请参阅问题的评论)是由另一个 AccountsController@Inject 引起的控制器。另见答案。

建议的解决方案确实适用于使用 Payara 4.1.1 163 将 @Stateless 类 注入 CDI bean。不过我在使用 GlassFish 4.1 时遇到了问题。

此外,当您将 bean 注入其他 bean 时应用此解决方案,例如:

@Named
@ViewScoped
public class OtherController extends AbstractController<Other> {

  private final AccountsController accountsController;

  @Inject
  public OtherController(AccountsController accountsController) {
    Assert.notNull(accountsController, "accountsController must not be null!");
    this.accountsController = accountsController;
  }

}

.. 你会得到这个错误(如问题的编辑 2 中所述):

WELD-001435: Normal scoped bean class ...AccountsController is not proxyable because it has no no-args constructor

N.B。 AccountsController 已按建议修改。

我使用 OmniFaces 解决了这个问题 Beans:

@Named
@ViewScoped
public class OtherController extends AbstractController<Other> {

  private final AccountsController accountsController;

  public OtherController() {
    this.accountsController = Beans.getInstance(AccountsController.class);
  }

}

...或者,对于完全不同的东西,您也可以只抑制 (CDI) 托管 bean 上的警告:

@SuppressWarnings("squid:S3306")