深入讲解 Laravel 的 IoC 服务容器
? 众所周知,Laravel 控制反转 (IoC) / 依赖注入 (DI) 的功能非常强大。遗憾的是, 官方文档 并没有详细讲解它的所有功能,所以我决定自己实践一下,并整理成文。下面的代码是基于 Laravel 5.4.26 的,其他版本可能会有所不同。 了解依赖注入我在这里不会详细讲解依赖注入/控制反转的原则 - 如果你对此还不是很了解,建议阅读 Fabien Potencier (Symfony 框架的创始人)的 What is Dependency Injection? 。 ? 访问容器通过 Laravel 访问 Container 实例的方式有很多种,最简单的就是调用辅助函数 为了突出重点 Container 类,这里就不赘述其他方式了。 注意: 官方文档中使用的是 (* 在 Laravel 应用中,Application 实际上是 Container 的一个子类 ( 这也说明了辅助函数 在 Laravel 之外使用 IlluminateContainer想要不基于 Laravel 使用 Container,安装 然后: 基础用法最简单的用法是通过构造函数注入依赖类。 使用 Container 的 container 会自动实例化依赖类,所以上面代码实现的功能就相当于: ( 假设 实战?phper在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家需要的(点击→)我的官方群677079770 下面是一些基于 PHP-DI 文档 的例子 - 将发送邮件与用户注册的代码解耦: 绑定接口与具体实现通过 Container 类,我们可以轻松实现从接口到具体类到实例的过程。首先定义接口: 声明实现接口的具体类,具体类还可以依赖其他接口( 或者是像上个例子中的具体类 ): 然后使用 最后,在 注意: 如果没有将接口与具体类进行绑定操作,就会报错: 这是因为 container 会尝试实例化接口 ( 实战可更换的缓存层: 绑定抽象类与具体类也可以与抽象类进行绑定: 或者将具体类与其子类进行绑定: 自定义绑定在使用 每次带着配置信息创建一个 MySQLDatabase 类的实例的时候( 下面后讲到如何通过 Singletons 创建一个可以共享的实例),都要用到 Database 接口。我们看到闭包函数接收了 Container 的实例作为参数,如果需要的话,还可以用它来实例化其他类: 还可以通过闭包函数自定义要如何实例化某个类: 解析回调函数可以使用 所有的注册的回调函数都会被调用。这种方法也适用于接口和抽象类: 还可以注册一个任何类被解析时都会被调用的回调函数 - 但是我想这可能仅适用于登录和调试: 扩展类你还可以使用 这里返回的另外一个类应该也实现了同样的接口,否则会报错。 单例绑定只要使用 或者是闭包: 为一个具体类创建单例,就只传这个类作为唯一的参数: 在以上的每种情况下,单例对象都是一次创建,反复使用。如果想要复用的实例已经生成了,则可以使用 自定义绑定的名称其实,你可以使用任意字符串作为绑定的名称,而不一定非要用类名或者接口名 - 但是这样做的弊端就是不能使用类名实例化了,而只能使用 为了同时支持类和接口,并且简化类名的写法,可以使用 存储值你也可以使用 container 来存储任何值 - 比如:配置数据: 支持以数组的形式存储: 在通过闭包进行绑定的时候,这种存储方式就显示出其好用之处了: ( Laravel 框架没有用 container 来存储配置文件,而是用了单独的 Config 类 - 但是 PHP-DI 用了) 小贴士: 在实例化对象的时候,还可以用数组的形式来代替 通过方法 / 函数做依赖注入到目前为止,我们已经看了很多通过构造函数进行依赖注入的例子,其实,Laravel 还支持对任何方法做依赖注入: 除了依赖类,还可以传其他参数: 可用于任何可调用的方法: 闭包静态方法普通方法调用实例方法的快捷方式通过这种语法结构 容器用于实例化类,这意味着:
例如,这将会启作用: 最后,你可以将「默认方法」作为第三个参数。如果第一个参数是一个没有指定方法的类名,则将调用默认的方法。 Laravel 使用 事件处理 来实现: 方法调用绑定可以使用 所有这些都会奏效,调用闭包而不是的原始方法: 但是, 注意: 这个方法不属于 容器接口,只是具体的 容器类. 参考 提交的 PR 了解为什么忽略参数。 上下文绑定有时候,你希望在不同的地方使用接口的不同实现。下面是来自 Laravel 文档 中的一个例子: 现在, PhotoController 和 VideoController 都可以依赖于文件系统接口,但是每个都将接收不同的实现。你还可以为 或者命名依赖项: 将参数绑定基本类型你还可以通过将变量名称传递给 您可以使用闭包来延迟检索值,直到需要它: 在这里你不能传递一个类或一个命名的依赖项(例如 标记你可以使用容器 然后将所有标记的实例检索为数组:
重新绑定*Note: 这是一个更高级的,只是很少需要-请随意跳过它! * 在绑定或实例已经被使用后需要更改时,可以调用 (有关重新绑定的更多信息,看 这里 和 这里.) refresh()还有一个快捷方法 它还返回现有实例或绑定(如果有的话),因此您可以这样做: (就个人而言,我发现这种语法更加混乱,并且更喜欢上面更详细的版本!) Note: 这些方法不属于 Container interface,只有具体 Container class. 覆盖构造函数参数
Note: 在Laravel 5.3及以下版本中,它很简单 其他方法这涵盖了我认为有用的所有方法 - 但只是为了解决问题,这里是剩下的公共方法的摘要...... bound()如果类或名称已与 它可以用 bindIf()
没有 或者这样写全也可以: resolved()如果已经解析了类 我不确定它有什么用处,如果使用 factory()
我不确定它有什么用处... wrap()
我不确定它有什么用处,因为闭包没有参数... Note: 这种方法不属于 Container interface,只属于 Container class. afterResolving()
最后...
Note: 最后一节中没有一个方法是其中的一部分 Container interface. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |