求详细点的解答,至今没想清楚
不仅仅是 hyperf ,现代化一些的框架, Laravel、ThinkPHP 等,这些都有类似的方法,如 Laravel 的 app()、app()->make() 等,其都归因为「容器」。
app()
app()->make()
举个简单的栗子,现在有一个需求,需要从数据库查询数据,需要一个 MySQLRepository 类,你可以在代码里直接写作:new MySQLRepository() 。就可以直接拿到 MySQLRepository 的实例,同样的 app(MySQLRepository::class) 或者 app()->make(MySQLRepository::class) ,也可以拿到这个 MySQLRepository 的实例。
new MySQLRepository()
app(MySQLRepository::class)
app()->make(MySQLRepository::class)
如果只是简单的使用,看不出来有什么区别,都是 new 一个实例,倘若现在, MySQLRepository 不能满足需求了,你需要换到 MongoDBRepository,这时候,如果你使用 new 的方式创建的实例,你就需要把每个地方的 new MySQLRepository 替换为 new MongoDBRepository。
new
new MySQLRepository
new MongoDBRepository
这下工作量可就大了,但是如果使用了上面的「容器」,那就可以简单的在容器上添加绑定即可。
app()->bind(MySQLRepository::class, MongoDBRepository::class);
现在,之前写的 app(MySQLRepository::class) ,创建的实例实际上就成了 MongoDBRepository,这就是对容器最基本的使用。当然,这只是举例,实际业务中我们应该使用接口来完成 Repository 的定义。
依赖注入,其实才是这个带来的最大的好处。
依赖注入
举个栗子,常常能看到一些控制器方法中,有一个 Request 的注入,那你有没有想过,为什么我写了个类型,他就有 Request ,我不写他就没有呢?这就是 IoC(依赖注入容器) 在背后给我们做的。
IoC(依赖注入容器)
它实现起来并不难,就是利用反射,来获取方法的参数,然后获得参数的类型,再使用容器或获取类型的实例(如果有绑定就返回绑定的),就这样,我们只写了一个简单的类型在那里,在使用的时候就成了实际的对象,这都是 IoC ,给我们做的的。
除此之外,还有更有趣的,绑定时我们还可以绑定一些初始化参数,比如,我们有一个 Ding 类,是一个钉钉机器人的类,一般情况下,我们都需要钉钉机器人的 SECRET 和 TOKEN,看起来像是下面这样。
$secert = config('ding.secert'); $token = config('ding.token'); $ding = new Ding($secert, $token); $ding->send('Hi');
然后我们每次需要发送消息的时候,都需要来这样操作一次,当然,你可能会想,我可以封装一个方法然后完成这些不就好了,确实也可以,那么,这里用容器来做一下试试。
app()->bind(Ding::class, function(){ $secert = config('ding.secert'); $token = config('ding.token'); return new Ding($secert, $token); });
现在,这个 Ding 类就可以像在控制器方法里面那样,直接添加一个类型提示,就可以直接获得一个 Ding 实例,然后调用 send 方法了。
//... class IndexController{ //... function index(Ding $ding){ $ding->send('Hi'); } }
相比起封装一个函数,它更加容易被测试,因为我们可以随时替换容器的绑定,进行模拟。
依赖倒置,是一个非常好的实践,但是,可能会出现这种情况,A 依赖 B,B 依赖 C, C 依赖 D, D 依赖 E ....
这时候如果我们想要一个 A 的对象,代码可能就会是这样 new A(new B(new C(new D()))),
new A(new B(new C(new D())))
如果使用容器,就只需要 app(A::class),这样就能完成上面那一堆操作,是的,他会自动帮你递归解析依赖然后注入进去。
app(A::class)
当然,实际情况可能比这些复杂,比如 B 可能是 A 的依赖项,也可能是 A2 的依赖项,而且在分别作为这两个的依赖项时,可能需要的参数还不一样,又或者,B 可能有出来依赖一个有类型的 A,还可能依赖一个常量值(基本值),这些都是有可能的,这时候又该怎么办?
这就涉及到了另一个知识点 上下文绑定,刚刚看了 Hyperf 的文档,没有提到这一块的内容,看了一下代码, hyperf/di 貌似也没有实现这个,那么可以参考一下 Laravel 文档中的 上下文绑定
上下文绑定
使用 app 或者 app()->make() 的好处除了上述的外,还有一些 new 不太具备的,比如更加容易的测试。
app
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
暂无简介
文章 0 评论 0
接受
发布评论
评论(1)
不仅仅是 hyperf ,现代化一些的框架, Laravel、ThinkPHP 等,这些都有类似的方法,如 Laravel 的
app()
、app()->make()
等,其都归因为「容器」。牛刀小试
举个简单的栗子,现在有一个需求,需要从数据库查询数据,需要一个 MySQLRepository 类,你可以在代码里直接写作:
new MySQLRepository()
。就可以直接拿到 MySQLRepository 的实例,同样的app(MySQLRepository::class)
或者app()->make(MySQLRepository::class)
,也可以拿到这个 MySQLRepository 的实例。如果只是简单的使用,看不出来有什么区别,都是 new 一个实例,倘若现在, MySQLRepository 不能满足需求了,你需要换到 MongoDBRepository,这时候,如果你使用
new
的方式创建的实例,你就需要把每个地方的new MySQLRepository
替换为new MongoDBRepository
。这下工作量可就大了,但是如果使用了上面的「容器」,那就可以简单的在容器上添加绑定即可。
现在,之前写的 app(MySQLRepository::class) ,创建的实例实际上就成了 MongoDBRepository,这就是对容器最基本的使用。当然,这只是举例,实际业务中我们应该使用接口来完成 Repository 的定义。
依赖注入
依赖注入
,其实才是这个带来的最大的好处。举个栗子,常常能看到一些控制器方法中,有一个 Request 的注入,那你有没有想过,为什么我写了个类型,他就有 Request ,我不写他就没有呢?这就是
IoC(依赖注入容器)
在背后给我们做的。它实现起来并不难,就是利用反射,来获取方法的参数,然后获得参数的类型,再使用容器或获取类型的实例(如果有绑定就返回绑定的),就这样,我们只写了一个简单的类型在那里,在使用的时候就成了实际的对象,这都是 IoC ,给我们做的的。
除此之外,还有更有趣的,绑定时我们还可以绑定一些初始化参数,比如,我们有一个 Ding 类,是一个钉钉机器人的类,一般情况下,我们都需要钉钉机器人的 SECRET 和 TOKEN,看起来像是下面这样。
然后我们每次需要发送消息的时候,都需要来这样操作一次,当然,你可能会想,我可以封装一个方法然后完成这些不就好了,确实也可以,那么,这里用容器来做一下试试。
现在,这个 Ding 类就可以像在控制器方法里面那样,直接添加一个类型提示,就可以直接获得一个 Ding 实例,然后调用 send 方法了。
相比起封装一个函数,它更加容易被测试,因为我们可以随时替换容器的绑定,进行模拟。
自动依赖处理
依赖倒置,是一个非常好的实践,但是,可能会出现这种情况,
A 依赖 B,B 依赖 C, C 依赖 D, D 依赖 E ....
这时候如果我们想要一个 A 的对象,代码可能就会是这样
new A(new B(new C(new D())))
,如果使用容器,就只需要
app(A::class)
,这样就能完成上面那一堆操作,是的,他会自动帮你递归解析依赖然后注入进去。当然,实际情况可能比这些复杂,比如 B 可能是 A 的依赖项,也可能是 A2 的依赖项,而且在分别作为这两个的依赖项时,可能需要的参数还不一样,又或者,B 可能有出来依赖一个有类型的 A,还可能依赖一个常量值(基本值),这些都是有可能的,这时候又该怎么办?
这就涉及到了另一个知识点
上下文绑定
,刚刚看了 Hyperf 的文档,没有提到这一块的内容,看了一下代码, hyperf/di 貌似也没有实现这个,那么可以参考一下 Laravel 文档中的 上下文绑定使用
app
或者app()->make()
的好处除了上述的外,还有一些 new 不太具备的,比如更加容易的测试。