首先,请参阅此答案以了解使用与不使用之间的区别@
在你的routes
file:
https://stackoverflow.com/a/34867199/4600 https://stackoverflow.com/a/34867199/4600
然后,正如所述Play 2.5.x 迁移文档 https://www.playframework.com/documentation/2.5.x/Migration25#Routes-generated-with-InjectedRoutesGenerator:
路线现已生成使用依赖注入 aware InjectedRoutesGenerator
,而不是之前的StaticRoutesGenerator
假设控制器是单例对象。
因此,从 Play 2.5.0 开始,控制器默认使用依赖注入,您不需要@
让他们使用依赖注入。
现在让我们看看您的情况发生了什么。首先我要说的是,构造函数注入是注入依赖项的首选方式。 Guice 甚至建议(作为最佳实践)将final
带有构造函数注入的字段最小化可变性 https://github.com/google/guice/wiki/MinimizeMutability。 Guice 文档还建议您尝试仅注入直接依赖项 https://github.com/google/guice/wiki/InjectOnlyDirectDependencies。在你的情况下,你正在使用application
访问一个configuration
。为什么不注入configuration
对象代替?这将使您的依赖关系更加清晰(这将使每个实例的测试更容易)。
因此,按照此建议,您的代码将被重写为:
package controllers;
import com.google.inject.Inject;
import play.Configuration;
import play.mvc.Controller;
import play.mvc.Result;
import services.AccountService;
import java.io.IOException;
public class AccountsController extends Controller {
private final Configuration configuration;
private final AccountService accountService;
private final String host;
private final int port;
private final String dbName;
@Inject
public AccountsController(Configuration configuration, AccountService accountService) {
this.configuration = configuration;
this.accountService = accountService;
// initialize config variables
this.host = configuration.getString("db.default.host");
this.port = configuration.getInt("db.default.port");
this.dbName = configuration.getString("db.default.dbname");
}
public Result createOneAccount() throws IOException {
return accountService.createOneAccount(request().body().asJson());
}
}
但为什么现场注入会中断呢?
我们首先需要了解对象初始化。根据Java 规范 http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.5:
在对新创建的对象的引用作为结果返回之前,将使用以下过程处理指定的构造函数以初始化新对象:
将构造函数的参数分配给为此构造函数调用新创建的参数变量。
如果此构造函数以同一类中另一个构造函数(使用 this)的显式构造函数调用(第 8.8.7.1 节)开始,则评估参数并使用这相同的五个步骤递归地处理该构造函数调用。如果该构造函数调用突然完成,则此过程也会出于同样的原因突然完成;否则,继续步骤 5。
此构造函数不会以显式构造函数调用同一类中的另一个构造函数(使用 this)开始。如果此构造函数用于除 Object 之外的类,则此构造函数将以显式或隐式调用超类构造函数(使用 super)开始。使用这五个步骤递归地评估参数并处理超类构造函数调用。如果该构造函数调用突然完成,则此过程也会出于同样的原因突然完成。否则,继续步骤 4。
执行此类的实例初始值设定项和实例变量初始值设定项,将实例变量初始值设定项的值按照类源代码中文本出现的从左到右的顺序分配给相应的实例变量。如果执行任何这些初始化程序导致异常,则不会再处理其他初始化程序,并且此过程会突然完成并出现相同的异常。否则,继续步骤 5。
执行该构造函数主体的其余部分。如果该执行突然完成,则该过程也会出于同样的原因突然完成。否则,该过程将正常完成。
特别注意步骤 4,它解释了变量是在对象初始化期间初始化的。
为什么这很重要?因为 Guice 首先创建对象(然后上面的所有步骤都会发生),然后执行注入绑定(请参阅吉斯引导程序 https://github.com/google/guice/wiki/Bootstrap and Guice注射点 https://github.com/google/guice/wiki/InjectionPoints更多细节)。因此,您的字段在对象初始化时需要变量(application
)尚未注入,导致NullPointerException
.