如何理解服务容器 `bind`, `make` 方法与辅助函数 `App ()` 方法?

读了文档跟几篇文章后,对服务容器的概念跟实践还是有点不清晰,所以请教一下,下面的描述也可能有基本概念认知的问题。

Laravel 中可以在服务提供者中注册一个服务容器绑定,具体可以绑定一个实例、单例,给定初始数据等,实现自动依赖注入,文档中有介绍,绑定(Binding)是在服务提供者中访问服务容器 $this->app 进行注册的,类似:

$this->app->bind('HelpSpot\API', function ($app) {
    return new HelpSpot\API($app->make('HttpClient'));
});

解析实例则通过:$this->app->make('Name')
参考这篇文章,注册好的 FileSystem 单例可以通过 app('files') 进行访问(暂时不考虑 Facade),也就是解析该单例?

问题1:两种解析方式的应用场景有区别吗?
make() 需要访问 $this->app 即服务容器,而 app() 函数也返回服务容器,并且接受类或接口名称参数来解析它,所以他们的区别只是 $this->app->make() 是在服务提供者中为其他绑定注入一个解析,而 app() 方法可以在任何地方进行解析,甚至在服务提供者中也可以 app()->make() 当然这很诡异也不规范。

问题2:app() 函数的实践?
app() 函数可以直接用类当参数,如 app(Foo::Class),自动注入依赖后返回该类的实例,所以实践是:在没有特定需求的时候,不需要注册,而是直接用 app() 自动注入依赖创建实例,或者完全是两个不同的场景?

《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
最佳答案

第一个问题

如果你看看框架代码你会发现,app()$this->app是同一个东西(Ioc容器).要用$this->app肯定是当前类有app这个成员变量才行,而app()是直接从容器的静态变量去取的.app()在文档中被描述为帮助函数,目的就是在你不能$this->app时使用.本来,这两个函数不分使用场景的,但是当你考虑测试的时候,你会发现使用$this->app会便于写测试.举个栗子:

我需要在某个类中使用容器

class SomeClass
{
    public function someFunc(){
        $app = app();
        $app->make(xxxx::class);
    }
}

在测试的时候,如果我只是想mock一个容器,那么我必须想办法修改app()让它返回我mock的容器.但是如果我这样写:

class SomeClass
{
    protected $app;
    public function __construct($app){
        $this->app = $app;
    }
    public function someFunc(){
        $this->app->make(xxxx::class);
    }
}

在测试时,当我要实例化这个SomeClass,我只需要像下面这样mock一个app传入构造函数就可以了:

$someObject = new SomeClass(mockapp());

第二个问题

你的结论是对的,得益于composer的自动加载,你完全可以在框架的任何地方直接new项目中的类.如果你没有注册,那么app(Some::class)就是帮你new一个对象而已.有些类在创建时,需要一些参数,这时候,你就可以注册到容器中.你在把类注册到容器时,你是在告诉容器,这个类应该怎么new.

希望能够帮到你:smile:

5年前 评论
讨论数量: 2

第一个问题

如果你看看框架代码你会发现,app()$this->app是同一个东西(Ioc容器).要用$this->app肯定是当前类有app这个成员变量才行,而app()是直接从容器的静态变量去取的.app()在文档中被描述为帮助函数,目的就是在你不能$this->app时使用.本来,这两个函数不分使用场景的,但是当你考虑测试的时候,你会发现使用$this->app会便于写测试.举个栗子:

我需要在某个类中使用容器

class SomeClass
{
    public function someFunc(){
        $app = app();
        $app->make(xxxx::class);
    }
}

在测试的时候,如果我只是想mock一个容器,那么我必须想办法修改app()让它返回我mock的容器.但是如果我这样写:

class SomeClass
{
    protected $app;
    public function __construct($app){
        $this->app = $app;
    }
    public function someFunc(){
        $this->app->make(xxxx::class);
    }
}

在测试时,当我要实例化这个SomeClass,我只需要像下面这样mock一个app传入构造函数就可以了:

$someObject = new SomeClass(mockapp());

第二个问题

你的结论是对的,得益于composer的自动加载,你完全可以在框架的任何地方直接new项目中的类.如果你没有注册,那么app(Some::class)就是帮你new一个对象而已.有些类在创建时,需要一些参数,这时候,你就可以注册到容器中.你在把类注册到容器时,你是在告诉容器,这个类应该怎么new.

希望能够帮到你:smile:

5年前 评论

第一个问题

如果你看看框架代码你会发现,app()$this->app是同一个东西(Ioc容器).要用$this->app肯定是当前类有app这个成员变量才行,而app()是直接从容器的静态变量去取的.app()在文档中被描述为帮助函数,目的就是在你不能$this->app时使用.本来,这两个函数不分使用场景的,但是当你考虑测试的时候,你会发现使用$this->app会便于写测试.举个栗子:

我需要在某个类中使用容器

class SomeClass
{
    public function someFunc(){
        $app = app();
        $app->make(xxxx::class);
    }
}

在测试的时候,如果我只是想mock一个容器,那么我必须想办法修改app()让它返回我mock的容器.但是如果我这样写:

class SomeClass
{
    protected $app;
    public function __construct($app){
        $this->app = $app;
    }
    public function someFunc(){
        $this->app->make(xxxx::class);
    }
}

在测试时,当我要实例化这个SomeClass,我只需要像下面这样mock一个app传入构造函数就可以了:

$someObject = new SomeClass(mockapp());

第二个问题

你的结论是对的,得益于composer的自动加载,你完全可以在框架的任何地方直接new项目中的类.如果你没有注册,那么app(Some::class)就是帮你new一个对象而已.有些类在创建时,需要一些参数,这时候,你就可以注册到容器中.你在把类注册到容器时,你是在告诉容器,这个类应该怎么new.

希望能够帮到你:smile:

5年前 评论

测试过发现一些简单的注入,如注入 Illuminate\Contracts\Config\Repository config 实例或者简单的自定义类的实例,即不需要绑定复杂的依赖注入时候,可以直接用 app() 创建实例,不预先注册。

一些包也会用到 Service Provider 进行发布创建配置、迁移文件或绑定一些 artisan 命令之类的。

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!