Laravel:差异App :: bind和App :: singleton


67

对于laravel在IOC容器和外墙方面提供的所有美好功能,我感到有些困惑。由于我不是一个经验丰富的程序员,因此学习变得不知所措。

我想知道这两个示例之间有什么区别:

  1. “ Foo”的外观,并通过 App::bind()

  2. “ Foo”的外观,并通过 App::singleton()

以我的最佳理解,Foo::method()$app->make['foo']->method()在第一个示例中重写Foo该类的多个实例,在第二个示例中,由于它是通过绑定的App::singleton(),因此Foo每次调用该对象上的Method时,都将返回的同一实例。

很抱歉,这个问题的答案很明显,但是我找不到关于此事的任何确认,也没有明确的解释。


45
不要为不了解而道歉。相信我,你并不孤单!
放松在塞浦路斯,2014年

Answers:


74

就是这样

一个非常简单的证明是测试行为。由于Laravel应用程序只是简单地扩展Illuminate\Container\Container,我们将仅使用容器(在我的情况下,我甚至仅将容器作为对我的composer.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方法。如果绑定注册为共享(即单例),则返回类实例,否则每次返回一个新实例。

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

顺便说一句,Container::singleton是一样的Container::bind设置为true第三个参数。


1
非常感谢,这正是我想要的!在何时使用单例对象以及何时最好实例化多个对象方面,我是否会要求大量资源?
Luuk Van Dongen 2014年

如果在整个请求中需要一个类的相同实例,则应考虑使用单例方法(例如,购物车),因为其他所有绑定都应该很好。
niclasleonbock 2014年

如果我错了,请纠正我,但是如果在通过App :: bind()绑定的基础类上使用了Facade,那么Laravel将实例化基础类的新对象吗?如果是这样,该对象本身是否可访问,因为每次使用外观上的方法时都会实例化该对象?
Luuk Van Dongen 2014年

很抱歉,我答复晚了。正如文档所指出的,facades仅“为应用程序的IoC容器中可用的类提供“静态”接口”()。
niclasleonbock 2014年

您能举一个关于绑定和单例差异的真实例子吗?
设计

14

即使基础绑定不是单例,外观也可以作为单例工作。

假设您有:

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

和:

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

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

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

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

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

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


但是有一个例外。如上面所示,大多数情况下,定义的getFacadeAccessor()返回字符串,但是它也可以返回objectSchema立面示例:

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

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

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


这就是为什么可以在需要时使用clearResolvedInstances()方法来删除存储的实例的原因,因此,如果您希望在Facade上的每次调用中获得一个新对象,则可以getFacadeAccessor()在返回具体字符串之前在方法中使用该方法。
Mohyaddin Alaoddin

1
您宁可使用clearResolvedInstance()(不使用“ s”)清除仅需要的内容,而不清除所有内容。不过,最好还是返回一个实例。
Gras Double

3

但是我在某处读到Laravel总是将通过立面调用的类视为单例吗?

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

我有一个演示类,通常通过

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

立面

受保护的静态函数getFacadeAccessor(){return'demo'; }

类本身看起来像这样

类演示 
    {

        私人$ value1;        
        私人$ value2;        

        公共函数setVal1($ value)
        {
            $ this-> value1 = $ value;
        }

        公共函数setVal2($ value)
        {
            $ this-> value2 = $ value;
        }

        公共函数getVals()
        {
            返回'Val 1:'。$ this-> value1 'Val 2:'。$ this-> value2;
        }   

    }

您告诉我,如果我在此类上使用外观,它将实例化该类的对象,然后在该对象上调用方法。

对接我进行了更多测试,发现这种非常奇怪的行为(至少对我而言):

如果我做

演示:: setVal1('13654');

演示:: setVal2('random string')

我不应该使用Demo :: getVals()来检索刚刚创建的值,应该吗?由于每次使用立面方法时,都会实例化一个新对象,一个对象如何检索另一个对象的属性?应该有三个不同的实例,但是我仍然能够从其他实例中检索属性...


Gras Double在他的解决方案中很好地回答了这个问题……“立面确实作为单例工作,即使基础绑定不是单例也是如此。” 因此,当您使用外墙时,无论使用的是bind()还是singleton(),行为都相同。仅当您使用App :: make()创建新对象时,差异才重要。
2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.