差异应用程序: : bind 和应用程序: : singleton

我对 Laravel 在 IOC 容器和立面方面提供的所有好东西感到有点困惑。因为我不是一个经验丰富的程序员,所以学起来很费劲。

我想知道,这两个例子有什么不同:

  1. 通过 App::bind()在容器中注册的‘ Foo’的正面

  2. 通过 App::singleton()在容器中注册的‘ Foo’的正面

据我所知,Foo::method()将被重写为 $app->make['foo']->method(),因此在第一个示例中将创建 Foo类的多个实例,而在第二个示例中,由于它是通过 App::singleton()绑定的,因此每次调用该对象上的 Method 时,将返回相同的 Foo实例。

如果这个问题的答案太明显,我很抱歉,但我找不到任何关于这个问题的证实,也没有任何地方能够明确解释这个问题。

41309 次浏览

就是这样。

一个非常简单的证明就是测试这种行为。由于 Laravel Application 只是简单地扩展了 Illuminate\Container\Container,因此我们将只使用容器(在我的例子中,我甚至只将容器作为依赖项添加到我的 comper.json)进行测试。

require __DIR__ . '/vendor/autoload.php';


class FirstClass
{
public $value;
}


class SecondClass
{
public $value;
}


// Test bind()
$container = new Illuminate\Container\Container();


$container->bind('FirstClass');


$instance = $container->make('FirstClass');
$instance->value = 'test';


$instance2 = $container->make('FirstClass');
$instance2->value = 'test2';


echo "Bind: $instance->value vs. $instance2->value\n";


// Test singleton()
$container->singleton('SecondClass');


$instance = $container->make('SecondClass');
$instance->value = 'test';


$instance2 = $container->make('SecondClass');
$instance2->value = 'test2'; // <--- also changes $instance->value


echo "Singleton: $instance->value vs. $instance2->value\n";

结果正如预期的那样:

Bind: test vs. test2

Singleton: test2 vs. test2

也许是个肮脏的证据,但的确是一个。

所有的魔力都在于 Container::make方法。 如果绑定被注册为 share (即单例) ,则返回类实例,否则每次都会返回一个新实例。

资料来源: https://github.com/laravel/framework/blob/4.2/src/Illuminate/Container/Container.php#L442

顺便说一句,Container::singletonContainer::bind相同,第三个参数设置为 true。

但是在某个地方我读到 Laravel 把通过 facade 调用的类视为单例对象?

因此,我遇到了这个问题:

我有一个演示类通常通过绑定

$this->app->bind('demo', function() { return new Demo(); }

And set up a facade

protected static function getFacadeAccessor() { return 'demo'; }

类本身看起来是这样的

class Demo
{
    

private $value1;
private $value2;


public function setVal1($value)
{
$this->value1 = $value;
}


public function setVal2($value)
{
$this->value2 = $value;
}


public function getVals()
{
return 'Val 1: ' . $this->value1 . ' Val 2: ' . $this->value2;
}
                        

}

您告诉我,如果我在这个类上使用 facade,它将实例化该类的一个对象,然后调用该对象上的方法。

但是我做了更多的测试,发现了这个非常奇怪的(至少对我来说)行为:

如果我做

演示: : setVal1(“13654”) ;

Demo::setVal2('random string')

I shouldn't be able to use Demo::getVals() to retrieve the values I just created, should I? Since every time a facade method is used a new object will be instantiated and how can one object retrieve properties of another object? There should be three different instances but still I'm able to retrieve the properties from those other instances...

即使底层绑定不是单实例,Facades 也可以作为单实例工作。

假设你有:

$app->bind('foo', 'FooConcrete'); // not a singleton

以及:

class Foo extends \Illuminate\Support\Facades\Facade {
protected static function getFacadeAccessor() { return 'foo'; }
}

然后这将像往常一样创建2个 FooConcrete实例:

app('foo');
app('foo');

但是这只会创建一个 FooConcrete实例并重用它:

Foo::someMethod();
Foo::someMethod();

这是因为 resolveFacadeInstance()存储已解析的实例。


但有一个例外。大多数时候,定义的 getFacadeAccessor()返回一个 绳子,如上所示,但是它也可以返回一个 对象。来自 Schema外观的例子:

protected static function getFacadeAccessor() {
return static::$app['db']->connection()->getSchemaBuilder();
}

在这种情况下,resolveFacadeInstance()不存储实例。

因此,如果 getFacadeAccessor()返回一个新实例,每次对 Facade 的调用也会创建一个新实例。