【问题标题】:Singleton binding constructed multiple times in Laravel在 Laravel 中多次构建单例绑定
【发布时间】:2019-04-07 09:17:02
【问题描述】:

在我的服务提供者中,我将一个类与单例绑定:

public function register()
{
    $this->app->singleton('ResourceContainer', function($app){
        return new ResourceContainer();
    });
}

laravel doumentation 说这个类将被解析一次并返回相同的对象:

单例方法将一个类或接口绑定到只应解析一次的容器中。一旦解决了单例绑定,相同的对象实例将在随后对容器的调用中返回:

但在我的应用程序中,ResourceContainer 的构造函数被调用了两次。

我想在服务提供者的启动方法中调用这个实例:

public function boot()
{
    $resourceContainer = $this->app->make('ResourceContainer');

然后我将类注入到控制器中:

public function index(ResourceContainer $container, $resource){

当我调试时,ResourceContainer 的构造函数被调用了两次。我的控制器中的对象与服务提供者的引导方法中的对象不同。

【问题讨论】:

    标签: laravel


    【解决方案1】:

    您可以改用::class 语法来消除此问题:

    $this->app->singleton(ResourceContainer::class, function($app){
        return new ResourceContainer();
    });
    

    并以与make() 相同的方式使用它:

    $resourceContainer = $this->app->make(ResourceContainer::class);
    

    问题是你正在处理两个不同的字符串:

    'ResourceContainer'
    'Foo\Bar\ResourceContainer'
    

    第一个是你在容器中绑定单例的键(字符串),第二个是完全限定的类名。在幕后,Laravel 的服务容器基本上是一个 key=>value 数组,它将字符串键映射到函数值,因此不同的字符串最终很重要。

    当您绑定'ResourceContainer' 时,$this->app->make('ResourceContainer') 将正确解析您的单例并按照您的预期运行。

    但是,当您对这样的方法使用自动依赖注入时:

    public function index(ResourceContainer $container, $resource) {}
    

    Laravel 使用反射来检查方法参数,因此它将参数类型视为完全限定的类名'Foo\Bar\ResourceContainer'(而不是'ResourceContainer')。当它检查该字符串的容器时,它没有找到匹配项。 Laravel 的另一个特点是它足够“聪明”地解决简单的依赖关系,即使它们没有显式绑定在容器中,所以在这里它实际上会确定它可以构建依赖关系(即使它没有在容器中找到匹配项)容器)并创建类的 new 实例,而不是引用单例,因为它的行为就像没有找到绑定一样。

    ::class 语法解决了所有这些问题,因为它返回完全限定的类名,因此所有内容都将引用容器中的相同键。

    【讨论】:

    • 这只是几秒钟的事情,但我同意你对它投赞成票。 ;)
    【解决方案2】:

    原因是您请求不同的对象。它基本上归结为这两行代码:

    $resourceContainer = $this->app->make('ResourceContainer');
    public function index(ResourceContainer $container) { ... } // simplified
    

    因为您很可能在全局命名空间中没有 ResourceContainer 类,但可能在 App\Services\ResourceContainer 之类的某个位置,这是将从服务容器请求的名称。所以类型提示一个类实际上会在后台调用$this->app->make('App\Services\ResourceContainer'),因为该类位于该命名空间中。

    因此解决方案相当简单:在其类名下注册单例并将手动调用更改为make($class)。类型提示开箱即用。

    public function register()
    {
        $this->app->singleton(ResourceContainer::class, function($app) {
            return new ResourceContainer();
        });
    }
    

    【讨论】:

      猜你喜欢
      • 2017-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多