[英]Play Framework 2.5.1 routing and dependency injection (for Java)
我在“路线”文件中有这个:
POST /accounts/ controllers.AccountsController.createOneAccount
在我的AccoutsController.java中:
package controllers;
import com.google.inject.Inject;
import play.Application;
import play.mvc.Controller;
import play.mvc.Result;
import services.AccountService;
import java.io.IOException;
public class AccountsController extends Controller {
@Inject
private Application application;
final String host = application.configuration().getString("db.default.host");
final int port = application.configuration().getInt("db.default.port");
final String dbName = application.configuration().getString("db.default.dbname");
@Inject
private AccountService accountService;
public Result createOneAccount() throws IOException {
return accountService.createOneAccount(request().body().asJson());
}
}
这段代码编译得很好,但在运行时我遇到这样的错误:
ProvisionException:无法配置,请参阅以下错误:1)在controllers.AccountsController注入构造函数,java.lang.NullPointerException时出错。(AccountsController.java:11)
同时定位controllers.AccountsController for router.Routes。(Routes.scala:28),同时定位router.Routes同时定位play.api.inject.RoutesProvider,同时在play.api找到参数0的play.api.routing.Router .http.JavaCompatibleHttpRequestHandler。(HttpRequestHandler.scala:200)同时定位play.api.http.JavaCompatibleHttpRequestHandler,同时在play.api.DefaultApplication。(Application.scala:221)中找到参数4的play.api.http.HttpRequestHandler。 api.DefaultApplication.class(Application.scala:221),同时定位play.api.DefaultApplication,同时定位play.api.Application 1错误
我可以通过将@添加到routes文件来解决这个问题:
POST /accounts/ @controllers.AccountsController.createOneAccount
但我不确定为什么我需要这样做,以及如何避免'@'。 请提出一些建议。
首先,看看这个答案,了解使用与否的差别@
在你的routes
文件:
https://stackoverflow.com/a/34867199/4600
然后,如Play 2.5.x迁移文档所述 :
现在使用依赖注入感知的
InjectedRoutesGenerator
生成路由,而不是之前的StaticRoutesGenerator
,它假设控制器是单例对象。
因此,从Play 2.5.0开始,控制器默认使用依赖注入,您不需要@
来使它们使用依赖注入。
现在让我们看看你的案例中发生了什么。 首先,我要说构造函数注入是注入依赖项的首选方法。 Guice甚至建议(作为最佳实践)将final
字段与构造函数注入相结合,以最大限度地减少可变性 。 Guice文档还建议您尝试仅注入直接依赖项 。 在您的情况下,您正在使用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规范 :
在作为结果返回对新创建的对象的引用之前,处理指示的构造函数以使用以下过程初始化新对象:
将构造函数的参数分配给此构造函数调用的新创建的参数变量。
如果此构造函数以同一个类中的另一个构造函数的显式构造函数调用(第8.8.7.1节)开头(使用此方法),则使用这五个相同步骤计算参数并以递归方式处理该构造函数调用。 如果该构造函数调用突然完成,则此过程突然完成,原因相同; 否则,继续步骤5。
此构造函数不以同一个类中的另一个构造函数的显式构造函数调用开头(使用此方法)。 如果此构造函数用于Object以外的类,则此构造函数将以超类构造函数的显式或隐式调用开始(使用super)。 使用这五个相同的步骤评估参数并递归处理超类构造函数调用。 如果该构造函数调用突然完成,则此过程突然完成,原因相同。 否则,继续执行步骤4。
为此类执行实例初始值设定项和实例变量初始值设定项,将实例变量初始值设定项的值按从左到右的顺序分配给相应的实例变量,在这些顺序中,它们以文本方式出现在类的源代码中 。 如果执行任何这些初始值设定项导致异常,则不会处理其他初始化程序,并且此过程会突然完成同样的异常。 否则,继续步骤5。
执行此构造函数的其余部分。 如果执行突然完成,则此过程突然完成,原因相同。 否则,此过程正常完成。
特别注意第4步,它解释了在对象初始化期间初始化变量。
为什么这很重要? 因为Guice首先创建对象(然后将发生上述所有步骤),然后执行注入绑定(有关详细信息,请参阅Guice Bootstrap和Guice InjectionPoints )。 因此,在对象初始化时,您的字段需要未注入的变量( application
),从而导致NullPointerException
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.