Laravel:区别 App::bind 和 App::singleton
Posted
技术标签:
【中文标题】Laravel:区别 App::bind 和 App::singleton【英文标题】:Laravel: Difference App::bind and App::singleton 【发布时间】:2014-10-03 10:37:14 【问题描述】:我对 laravel 在 IOC 容器和外观方面提供的所有好处感到有些困惑。因为我不是一个经验丰富的程序员,所以学习起来会让人不知所措。
我在想,这两个例子有什么区别:
“Foo”的外观并通过App::bind()
注册在容器中
“Foo”的外观并通过App::singleton()
注册在容器中
在我的最佳理解中,Foo::method()
将被重写为 $app->make['foo']->method()
,因此在第一个示例中,将创建 Foo
类的多个实例,而在第二个示例中,因为它是通过 App::singleton()
绑定的,所以相同每次调用该对象上的方法时,都会返回 Foo
的实例。
如果这个问题的答案很明显,我很抱歉,但我找不到任何关于这个问题的确认,也没有明确解释。
【问题讨论】:
不要因为不理解而道歉。相信我,你并不孤单! 【参考方案1】:就是这样。
一个非常简单的证明是测试行为。由于 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。
【讨论】:
非常感谢,这正是我想要的!关于何时使用单例以及何时最好实例化多个对象,我是否需要一个很好的资源? 如果您在整个请求中需要一个类的相同实例,您应该考虑使用单例方法(例如购物车),因为其他一切都可以绑定。 如果我错了,请纠正我,但是如果在通过 App::bind() 绑定的底层类上使用了外观,那么 Laravel 会实例化底层类的新对象吗?如果是这样,该对象本身是否可访问,因为每次使用外观上的方法时都会实例化它? 抱歉我的回复晚了。正如文档指出的那样,外观仅“为应用程序的 IoC 容器中可用的类提供“静态”接口”(source)。 你能举一个真实的例子来说明绑定和单例的区别吗?【参考方案2】:但是我在某处读到 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 。 ' 值 2:' 。 $this->value2;
你告诉我,如果我要在这个类上使用外观,它会实例化该类的一个对象,然后调用该对象上的方法。
但我又测试了一些,发现这种(至少对我而言)非常奇怪的行为:
如果我这样做了
Demo::setVal1('13654');和
Demo::setVal2('随机字符串')
我不应该能够使用 Demo::getVals() 来检索我刚刚创建的值,不是吗?由于每次使用外观方法都会实例化一个新对象,一个对象如何检索另一个对象的属性?应该有三个不同的实例,但我仍然能够从其他实例中检索属性...
【讨论】:
Gras Double 在他的解决方案中很好地回答了这个问题......“即使底层绑定不是单例,外墙也可以作为单例工作。”因此,当您使用外观时,使用 bind() 或 singleton() 都没有关系,无论哪种方式,行为都是相同的。差异仅在您创建新对象时才重要,例如使用 App::make()。【参考方案3】:即使底层绑定不是单例,门面也可以作为单例工作。
假设你有:
$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
Facade 的示例:
protected static function getFacadeAccessor()
return static::$app['db']->connection()->getSchemaBuilder();
在这种情况下,resolveFacadeInstance()
不会存储实例。
因此,如果getFacadeAccessor()
返回一个新实例,则对 Facade 的每次调用也会创建一个新实例。
【讨论】:
这就是为什么如果需要你可以使用clearResolvedInstances()
方法来删除存储的实例,所以如果你想在 Facade 上的每次调用中获取一个新对象,你可以在 @返回具体字符串之前的 987654335@ 方法。
您应该使用clearResolvedInstance()
(没有“s”)只清除您需要的内容,而不是所有内容。不过,最好只返回一个实例。以上是关于Laravel:区别 App::bind 和 App::singleton的主要内容,如果未能解决你的问题,请参考以下文章