如何理解服务容器 `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() 自动注入依赖创建实例,或者完全是两个不同的场景?


为什么标题的 app 会自动首字母大写,还加了空格?

Zhibin
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
最佳答案

第一个问题

如果你看看框架代码你会发现,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:

1年前 评论
讨论数量: 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:

1年前 评论

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

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

1年前 评论

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!

社区文档:

将托管在 packagist.org 和 github.com 的扩展包使用国内 CDN 加速
GitHub Laravel 扩展包 TOP 250
速查表方便快速查询框架功能,支持手机访问,支持中英文版本
Laravel 中文文档,由社区用户翻译和维护,将会保持一直更新
此文档的目的,就是为了提高技术团队的凝聚力、一致性和生产效率。
开发环境的部署,开发者工具的选择,适用于 Mac 和 Windows。
浓缩过后的精华
Laravel Nova 后台管理面板文档的中文翻译
Lumen 中文文档,由社区用户翻译和维护,将会保持一直更新
Laravel 下知名扩展包 Dingo API 的中文文档,Laravel API 开发必知必会