【问题标题】:Laravel Difference `between app->bind` and `app->singleton`?Laravel“app->bind”和“app->singleton”之间的区别?
【发布时间】:2014-11-06 00:02:17
【问题描述】:

在 Laravel 中设置服务提供者时,我一直试图弄清楚 app->bindapp->singleton 之间的区别。我的印象是,如果我注册一个 singleton,每次调用它都会返回相同的对象实例,而 bind 将是一个新实例。

这是一个简单的例子:

立面:

use Illuminate\Support\Facades\Facade;

class DataFacade extends Facade
{
    protected static function getFacadeAccessor() { 
        return 'Data';
    }
}

服务提供者:

use Illuminate\Support\ServiceProvider;

class DataServiceProvider extends ServiceProvider
{
    public function register() {
        $this->app->singleton('Data', function() {
            return new Data;
        });
    }
}

类:

class Data
{
    public $data = [];

    public function get($key)
    {
        return isset($this->data[$key]) ? $this->data[$key] : null;
    }

    public function set($key, $val)
    {
        $this->data[$key] = $val;
    }
}

如果我们这样做:

$instance = App::make('Data');
$instance->set('foo', 'foo');

$instance2 = App::make('Data');

echo $instance->get('foo');
echo $instance2->get('foo');

然后运行,我们将看到bindsingleton 之间的适当行为,foo 分别打印一次和两次。但是,如果我们像这样通过外观运行它:

Data::set('test', 'test');
Data::set('cheese', 'cheese');

当它是单例时,我希望 testcheese 都可用,而当它是 bind 时,我不确定我希望通过门面获得什么,但似乎有没有区别。

是外观把所有东西都当作singleton

【问题讨论】:

  • 你已经知道区别了!
  • 你提到了bind——但你的问题从不直接调用bind。
  • 但是门面不是调用调用bind的服务提供者吗?
  • @Rob 答案在下面——在 4.2 中(不知道早期版本),看起来门面调用了 $app[$service_name],而后者又调用了 make。更令人困惑的是,Facade 还有自己的单例存储层。

标签: laravel laravel-4 singleton facade


【解决方案1】:

您的问题有点令人困惑,没有人可以回答的所有信息,但这是一个令人困惑的话题,所以不要难过。这里有一个纲要,可以帮助你更好地理解,并提出你想问的问题(另外,我对 Laravel 很陌生,所以我可能不了解这些)

  1. make 方法用于实例化对象。当你说 App::make('Data') 时,你是在告诉 Laravel 从类 Data 中实例化一个对象。

  2. 对数字 1 有一个警告。如果您调用 make 并且已经将字符串 Data 绑定到服务容器中的某个内容,Laravel 将改为返回该服务。这可能意味着 Laravel 实例化了一个新的服务对象,也可能意味着 Laravel 返回一个服务单例

  3. Laravel 是否为服务返回单例或实例取决于服务的绑定方式

  4. make 方法不绑定任何东西

  5. 您将服务与应用程序对象的bind 方法绑定,该方法在容器类上定义,方法原型为public function bind($abstract, $concrete = null, $shared = false)

  6. 看到第三个$shared 参数了吗?如果这是真的,您的服务将返回一个单例。如果为 false,您的服务将返回实例。

  7. 应用对象的singleton方法是绑定服务的方法

回复:#7,这是singleton的定义

#File: vendor/laravel/framework/src/Illuminate/Container/Container.php
public function singleton($abstract, $concrete = null)
{
    $this->bind($abstract, $concrete, true);
}

在上面的示例中,您将服务Data 绑定到容器中。使用领先的案例服务名称会导致问题——data 会是更好的选择。如果你的 register 方法由于某种原因没有被调用,make 仍然会用你的全局类 Data 实例化一个对象

关于您的外观——外观是实例/单一性的额外层。这是外观类使用来自getFacadeAccessor 的字符串从静态调用返回对象的方法

#File: vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
protected static function resolveFacadeInstance($name)
{
    if (is_object($name)) return $name;

    if (isset(static::$resolvedInstance[$name]))
    {
        return static::$resolvedInstance[$name];
    }

    return static::$resolvedInstance[$name] = static::$app[$name];
}

因此,外观使用$app[$name]; 从容器中获取服务。这是ArrayAccess,所以如果我们看一下offsetGet的定义

public function offsetGet($key)
{
    return $this->make($key);
}

我们看到ArrayAccess 包装了对make 的调用。这意味着如果您没有绑定服务,外观访问将实例化一个对象。如果您将服务绑定为单例/共享服务,则外观访问将返回该单例。如果您将服务绑定为非单例/共享服务,则外观访问将实例化一个新对象。

但是,Facade 本身会将它实例化的任何对象存储在static::$resolvedInstance 中,并且将来对外观的调用将返回相同的实例。这意味着 Facade 访问引入了第二个单例实现。绑定为单例的服务将存储在应用程序对象中,通过外观访问的服务将作为单例存储在 Facade 类中。

【讨论】:

  • 感谢您的精彩解释,这有助于澄清很多事情。我错过了关于立面上单身人士的文章。如果我理解正确,请参考我上面的示例,所以如果外观创建一个新实例,它将是一个单例,它总是引用它通过 IoC/服务提供者绑定创建的$instance1?这是否意味着无论您的服务提供者是否设置为新实例/单例,外观将始终“充当”单例?如果是这样,有没有办法使用 Facade 解决它?我应该补充一下,是否有我们想要的场景?
  • @Rob -- 我还不是专家,但是是的,看起来 Facade 会实例化一个对象一次,然后总是使用同一个对象进行方法调用,不管 容器是如何绑定的。您可以使用执行return $this 的服务方法对此进行测试,通过外观调用该方法,然后进行状态检查。我的怀疑是这是/是一个故意的决定,以避免“服务是默认情况下的实例而不是单例”的性能问题
  • 我认为外观在这里会造成混淆,它们总是强制单例而不是多个实例,即使您使用 shared false 绑定。
猜你喜欢
  • 2014-10-03
  • 2015-08-02
  • 2014-07-18
  • 2014-07-01
  • 2023-03-04
  • 2019-03-17
  • 1970-01-01
  • 2012-12-26
  • 2016-10-12
相关资源
最近更新 更多