Play Framework 2.5.1路由和依赖项注入(适用于Java) [英] Play Framework 2.5.1 routing and dependency injection (for Java)

查看:105
本文介绍了Play Framework 2.5.1路由和依赖项注入(适用于Java)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的路线"文件中有这个文件:

I have this in my "routes" file:

POST        /accounts/        controllers.AccountsController.createOneAccount

在我的AccoutsController.java中:

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

这段代码可以很好地编译,但是在运行时出现如下错误:

This code is compiling fine, but in runtime I got error like this:

ProvisionException:无法设置,请参见以下错误:1) 在以下位置注入构造函数java.lang.NullPointerException时出错 controllers.AccountsController.(AccountsController.java:11)​​
在定位controllers.AccountsController时 定位路由器时在router.Routes.(Routes.scala:28)上获取参数1的位置,同时定位play.api.inject.RoutesProvider的路由 在定位play.api.routing.Router时 用于play.api.http.JavaCompatibleHttpRequestHandler的参数0(HttpRequestHandler.scala:200) 在定位play.api.http.JavaCompatibleHttpRequestHandler的同时 定位play.api.http.HttpRequestHandler 对于play.api.DefaultApplication.(Application.scala:221)处的参数4 play.api.DefaultApplication.class(Application.scala:221)而 定位时定位play.api.DefaultApplication play.api.Application 1错误

ProvisionException: Unable to provision, see the following errors: 1) Error injecting constructor, java.lang.NullPointerException at controllers.AccountsController.(AccountsController.java:11)
while locating controllers.AccountsController for parameter 1 at router.Routes.(Routes.scala:28) while locating router.Routes while locating play.api.inject.RoutesProvider while locating play.api.routing.Router for parameter 0 at play.api.http.JavaCompatibleHttpRequestHandler.(HttpRequestHandler.scala:200) while locating play.api.http.JavaCompatibleHttpRequestHandler while locating play.api.http.HttpRequestHandler for parameter 4 at play.api.DefaultApplication.(Application.scala:221) at play.api.DefaultApplication.class(Application.scala:221) while locating play.api.DefaultApplication while locating play.api.Application 1 error

我可以通过在路由文件中添加@来解决此问题:

I can resolve this by adding @ to routes file:

POST        /accounts/        @controllers.AccountsController.createOneAccount

但是我不确定为什么需要这样做,以及如何避免使用'@'.请提供一些建议.

but I am not sure about why I need to do this, and how to avoid the '@'. Please give some suggestions.

推荐答案

首先,请参见以下答案以了解在您的routes文件中使用或不使用@之间的区别:

First, see this answer to understand the difference between using or not @ in your routes file:

https://stackoverflow.com/a/34867199/4600

然后,如播放2.5.x所述迁移文档:

现在使用依赖注入(InjectedRoutesGenerator)而不是以前的StaticRoutesGenerator来生成路由,而以前的StaticRoutesGenerator假定控制器是单例对象.

Routes are now generated using the dependency injection aware InjectedRoutesGenerator, rather than the previous StaticRoutesGenerator which assumed controllers were singleton objects.

因此,从Play 2.5.0开始,控制器默认情况下使用依赖注入,您无需@即可使它们使用依赖注入.

So, starting at Play 2.5.0, controllers use dependency injection by default and you don't need @ to make them use dependency injection.

现在,让我们看看您的情况如何.首先,让我说构造函数注入是注入依赖项的首选方法. Guice甚至建议(作为最佳实践)将final字段与构造函数注入结合在一起,以实现最小化可变性. Guice文档还建议您尝试仅注入直接依赖项.在您的情况下,您正在使用application访问configuration.为什么不注入configuration对象呢?这将使您的依赖关系更加清晰(每个实例将使测试更加容易).

Now lets see what is happening in your case. First of all, let me say that constructor injection is the preferred way of injecting dependencies. Guice even recommends (as a best practice) to combine final fields with constructor injection to minimize mutability. Guice docs also recommends that you try to inject only direct dependencies. In your case, you are using application to access a configuration. Why not inject the configuration object instead? This will make your dependencies more clear (which will make, per instance, testing easier).

因此,按照此建议,您的代码将被重写为:

So, following this recommendations, your code would be rewritten to:

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规范 :

就在返回对新创建对象的引用作为结果之前,使用以下过程处理所指示的构造函数以初始化新对象:

Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:

  1. 为此构造函数调用将构造函数的参数分配给新创建的参数变量.

  1. Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.

如果此构造函数以同一个类中的另一个构造函数的显式构造函数调用(第8.8.7节)开头(使用此方法),则使用这五个步骤评估参数并递归处理该构造函数调用.如果该构造函数调用突然完成,则出于相同原因,此过程也会突然完成;否则,此过程将立即完成.否则,请继续执行步骤5.

If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5.

此构造函数并不以对同一个类中的另一个构造函数的显式构造函数调用(使用此函数)开头.如果此构造函数用于Object以外的其他类,则此构造函数将以显式或隐式调用超类构造函数(使用super)开头.使用这五个相同的步骤,递归评估超类构造函数调用的参数和过程.如果该构造函数调用突然完成,则出于相同原因,此过程也会突然完成.否则,请继续执行步骤4.

This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with an explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.

执行此类的实例初始值设定项和实例变量初始值设定项,将实例变量初始值设定项的值按从左到右的顺序从左到右分配给相应的实例变量.该课程的代码.如果执行这些初始化程序中的任何一个都会导致异常,则不会再处理其他初始化程序,并且该过程会因相同的异常而突然完成.否则,请继续执行步骤5.

Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5.

执行此构造函数的其余部分.如果该执行突然完成,则出于相同原因,此过程也会突然完成.否则,此过程将正常完成.

Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.

特别注意步骤4,该步骤说明了在对象初始化期间初始化了变量.

Special attention to step 4 which explains that your variables are initialized during the object initialization.

为什么这很重要?因为Guice首先创建对象(然后将执行上述所有步骤),然后再执行注入绑定(请参见 Guice Bootstrap Guice InjectionPoints 了解更多信息).因此,您的字段要求在对象初始化时使用尚未注入但导致NullPointerException的变量(application).

Why is this important? Because Guice first create objects (and then all the steps above will happen) and later performs the injection binds (see Guice Bootstrap and Guice InjectionPoints for more details). So, your fields are requiring, at object initialization, variables (application) that aren't injected yet resulting in a NullPointerException.

这篇关于Play Framework 2.5.1路由和依赖项注入(适用于Java)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆