详解Laravel服务容器的绑定与解析

吾爱主题 阅读:163 2021-09-16 15:35:00 评论:0

前言

  老实说,第一次老大让我看laravel框架手册的那天早上,我是很绝望的,因为真的没接触过,对我这种渣渣来说,laravel的入门门槛确实有点高了,但还是得硬着头皮看下去(虽然到现在我还有很多没看懂,也没用过)。
  后面慢慢根据公司项目的代码对laravel也慢慢熟悉起来了,但还是停留在一些表面的功能,例如依赖注入,ORM操作,用户认证这些和我项目业务逻辑相关的操作,然后对于一些架构基础的,例如服务提供器,服务容器,中间件,Redis等这些一开始就要设置好的东西,我倒是没实际操作过(因为老大一开始就做好了),所以看手册还是有点懵。
  所以有空的时候逛逛论坛,搜下Google就发现许多关于laravel核心架构的介绍,以及如何使用的网站(确实看完后再去看手册就好理解多了),下面就根据一个我觉得不错的网站上面的教学来记录一下laravel核心架构的学习
网站地址:https://laraweb.net/ 这是一个日本的网站,我觉得挺适合新手的,内容用浏览器翻译过来就ok了,毕竟日文直翻过来很好理解的

关于服务容器

  手册上是这样介绍的:Laravel 服务容器是用于管理类的依赖和执行依赖注入的工具。依赖注入这个花俏名词实质上是指:类的依赖项通过构造函数,或者某些情况下通过「setter」方法「注入」到类中。。。。。。(真的看不懂啥意思)
  服务容器是用于管理类(服务)的实例化的机制。直接看看服务容器怎么用

  1.在服务容器中注册类(bind)

?
1 2 $this ->app->bind( 'sender' , 'MailSender' ); //$this->app成为服务容器。

 

  2.从服务容器生成类(make)

?
1 2 $sender = $this ->app->make( 'sender' ); //从服务容器($this->app)创建一个sender类。

在这种情况下,将返回MailSender的实例。

  这是服务容器最简单的使用,下面是对服务容器的详细介绍

laravel容器基本认识

  一开始,index.php 文件加载 Composer 生成定义的自动加载器,然后从 bootstrap/app.php 脚本中检索 Laravel 应用程序的实例。Laravel 本身采取的第一个动作是创建一个 application/ service container 的实例。

?
1 2 3 $app = new Illuminate\Foundation\Application(    dirname(__DIR__) );

  这个文件在每一次请求到达laravel框架都会执行,所创建的$app即是laravel框架的应用程序实例,它在整个请求生命周期都是唯一的。laravel提供了很多服务,包括认证,数据库,缓存,消息队列等等,$app作为一个容器管理工具,负责几乎所有服务组件的实例化以及实例的生命周期管理。当需要一个服务类来完成某个功能的时候,仅需要通过容器解析出该类型的一个实例即可。从最终的使用方式来看,laravel容器对服务实例的管理主要包括以下几个方面:

  • 服务的绑定与解析
  • 服务提供者的管理
  • 别名的作用
  • 依赖注入

先了解如何在代码中获取到容器实例,再学习上面四个关键

如何在代码中获取到容器实例

第一种是

?
1 2 $app = app(); //app这个辅助函数定义在\vendor\laravel\framework\src\Illuminate\Foundation\helper.php

里面,,这个文件定义了很多help函数,并且会通过composer自动加载到项目中。

所以,在参与http请求处理的任何代码位置都能够访问其中的函数,比如app()。

第二种是

?
1 2 3 4 5 Route::get( '/' , function () {    dd(App::basePath());    return '' ; }); //这个其实是用到Facade,中文直译貌似叫门面,在config/app.php中,

有一节数组aliases专门用来配置一些类型的别名,第一个就是'App' => Illuminate\Support\Facades\App::class,

具体的Google一下laravel有关门面的具体实现方式

第三种是

  在服务提供者里面直接使用$this->app。服务提供者后面还会介绍,现在只是引入。因为服务提供者类都是由laravel容器实例化的,这些类都继承自Illuminate\Support\ServiceProvider,它定义了一个实例属性$app:

?
1 2 3 abstract class ServiceProvider {    protected $app ;

  laravel在实例化服务提供者的时候,会把laravel容器实例注入到这个$app上面。所以我们在服务提供者里面,始终能通过$this->$app访问到laravel容器实例,而不需要再使用app()函数或者App Facade了。

如何理解服务绑定与解析

  浅义层面理解,容器既然用来存储对象,那么就要有一个对象存入跟对象取出的过程。这个对象存入跟对象取出的过程在laravel里面称为服务的绑定与解析。

?
1 2 3 4 5 6 7 8 9 10 11 app()->bind( 'service' , 'this is service1' ); app()->bind( 'service2' , [    'hi' => function (){      //say hi    } ]); class Service { } app()->bind( 'service3' , function (){    return new Service(); });

  还有一个单例绑定singleton,是bind的一种特殊情况(第三个参数为true),绑定到容器的对象只会被解析一次,之后的调用都返回相同的实例

?
1 2 3 4 public function singleton( $abstract , $concrete = null) { $this ->bind( $abstract , $concrete , true); }

  在绑定的时候,我们可以直接绑定已经初始化好的数据(基本类型、数组、对象实例),还可以用匿名函数来绑定。用匿名函数的好处在于,这个服务绑定到容器以后,并不会立即产生服务最终的对象,只有在这个服务解析的时候,匿名函数才会执行,此时才会产生这个服务对应的服务实例。

  实际上,当我们使用singleton,bind方法以及数组形式,(这三个方法是后面要介绍的绑定的方法),进行服务绑定的时候,如果绑定的服务形式,不是一个匿名函数,也会在laravel内部用一个匿名函数包装起来,这样的话, 不轮绑定什么内容,都能做到前面介绍的懒初始化的功能,这对于容器的性能是有好处的。这个可以从bind的源码中看到一些细节:

?
1 2 3 if (! $concrete instanceof Closure) {    $concrete = $this ->getClosure( $abstract , $concrete ); }

看看bind的底层代码

?
1 public function bind( $abstract , $concrete = null, $shared = false)

 

  第一个参数服务绑定名称,第二个参数服务绑定的结果(也就是闭包,得到实例),第三个参数就表示这个服务是否在多次解析的时候,始终返回第一次解析出的实例(也就是单例绑定singleton)。

  服务绑定还可以通过数组的方式:

?
1 2 3 app()[ 'service' ] = function (){    return new Service(); };

 

绑定大概就这些,接下来看解析,也就是取出来用

?
1 $service = app()->make( 'service' );

 

  这个方法接收两个参数,第一个是服务的绑定名称和服务绑定名称的别名,如果是别名,那么就会根据服务绑定名称的别名配置,找到最终的服务绑定名称,然后进行解析;第二个参数是一个数组,最终会传递给服务绑定产生的闭包。

看源码:

 

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 /**   * Resolve the given type from the container.   *   * @param string $abstract   * @param array $parameters   * @return mixed   */ public function make( $abstract , array $parameters = []) {    return $this ->resolve( $abstract , $parameters ); } /**   * Resolve the given type from the container.   *   * @param string $abstract   * @param array $parameters   * @return mixed   */ protected function resolve( $abstract , $parameters = []) {    $abstract = $this ->getAlias( $abstract );    $needsContextualBuild = ! empty ( $parameters ) || ! is_null (      $this ->getContextualConcrete( $abstract )    );    // If an instance of the type is currently being managed as a singleton we'll    // just return an existing instance instead of instantiating new instances    // so the developer can keep using the same objects instance every time.    if (isset( $this ->instances[ $abstract ]) && ! $needsContextualBuild ) {      return $this ->instances[ $abstract ];    }    $this ->with[] = $parameters ;    $concrete = $this ->getConcrete( $abstract );    // We're ready to instantiate an instance of the concrete type registered for    // the binding. This will instantiate the types, as well as resolve any of    // its "nested" dependencies recursively until all have gotten resolved.    if ( $this ->isBuildable( $concrete , $abstract )) {      $object = $this ->build( $concrete );    } else {      $object = $this ->make( $concrete );    }    // If we defined any extenders for this type, we'll need to spin through them    // and apply them to the object being built. This allows for the extension    // of services, such as changing configuration or decorating the object.    foreach ( $this ->getExtenders( $abstract ) as $extender ) {      $object = $extender ( $object , $this );    }    // If the requested type is registered as a singleton we'll want to cache off    // the instances in "memory" so we can return it later without creating an    // entirely new instance of an object on each subsequent request for it.    if ( $this ->isShared( $abstract ) && ! $needsContextualBuild ) {      $this ->instances[ $abstract ] = $object ;    }    $this ->fireResolvingCallbacks( $abstract , $object );    // Before returning, we will also set the resolved flag to "true" and pop off    // the parameter overrides for this build. After those two things are done    // we will be ready to return back the fully constructed class instance.    $this ->resolved[ $abstract ] = true;    array_pop ( $this ->with);    return $object ; }

第一步:

?
1 2 3 $needsContextualBuild = ! empty ( $parameters ) || ! is_null (    $this ->getContextualConcrete( $abstract ) );

 

  该方法主要是区分,解析的对象是否有参数,如果有参数,还需要对参数做进一步的分析,因为传入的参数,也可能是依赖注入的,所以还需要对传入的参数进行解析;这个后面再分析。

第二步:

?
1 2 3 if (isset( $this ->instances[ $abstract ]) && ! $needsContextualBuild ) {    return $this ->instances[ $abstract ]; }

 

  如果是绑定的单例,并且不需要上面的参数依赖。我们就可以直接返回 $this->instances[$abstract]。

第三步:

 

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 $concrete = $this ->getConcrete( $abstract ); ... /**   * Get the concrete type for a given abstract.   *   * @param string $abstract   * @return mixed  $concrete   */ protected function getConcrete( $abstract ) {    if (! is_null ( $concrete = $this ->getContextualConcrete( $abstract ))) {      return $concrete ;    }    // If we don't have a registered resolver or concrete for the type, we'll just    // assume each type is a concrete name and will attempt to resolve it as is    // since the container should be able to resolve concretes automatically.    if (isset( $this ->bindings[ $abstract ])) {      return $this ->bindings[ $abstract ][ 'concrete' ];    }    return $abstract ; }

  这一步主要是先从绑定的上下文找,是不是可以找到绑定类;如果没有,则再从 $bindings[] 中找关联的实现类;最后还没有找到的话,就直接返回 $abstract 本身。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // We're ready to instantiate an instance of the concrete type registered for // the binding. This will instantiate the types, as well as resolve any of // its "nested" dependencies recursively until all have gotten resolved. if ( $this ->isBuildable( $concrete , $abstract )) {    $object = $this ->build( $concrete ); } else {    $object = $this ->make( $concrete ); } ... /**   * Determine if the given concrete is buildable.   *   * @param mixed  $concrete   * @param string $abstract   * @return bool   */ protected function isBuildable( $concrete , $abstract ) {    return $concrete === $abstract || $concrete instanceof Closure; }

 

  如果之前找到的 $concrete 返回的是 $abstract 值,或者 $concrete 是个闭包,则执行 $this->build($concrete),否则,表示存在嵌套依赖的情况,则采用递归的方法执行 $this->make($concrete),直到所有的都解析完为止。

 

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 $this ->build( $concrete )     /**   * Instantiate a concrete instance of the given type.   *   * @param string $concrete   * @return mixed   *   * @throws \Illuminate\Contracts\Container\BindingResolutionException   */ public function build( $concrete ) {    // If the concrete type is actually a Closure, we will just execute it and    // hand back the results of the functions, which allows functions to be    // used as resolvers for more fine-tuned resolution of these objects.    // 如果传入的是闭包,则直接执行闭包函数,返回结果    if ( $concrete instanceof Closure) {      return $concrete ( $this , $this ->getLastParameterOverride());    }    // 利用反射机制,解析该类。    $reflector = new ReflectionClass( $concrete );    // If the type is not instantiable, the developer is attempting to resolve    // an abstract type such as an Interface of Abstract Class and there is    // no binding registered for the abstractions so we need to bail out.    if (! $reflector ->isInstantiable()) {      return $this ->notInstantiable( $concrete );    }    $this ->buildStack[] = $concrete ;    // 获取构造函数    $constructor = $reflector ->getConstructor();    // If there are no constructors, that means there are no dependencies then    // we can just resolve the instances of the objects right away, without    // resolving any other types or dependencies out of these containers.    // 如果没有构造函数,则表明没有传入参数,也就意味着不需要做对应的上下文依赖解析。    if ( is_null ( $constructor )) {      // 将 build 过程的内容 pop,然后直接构造对象输出。      array_pop ( $this ->buildStack);      return new $concrete ;    }    // 获取构造函数的参数    $dependencies = $constructor ->getParameters();    // Once we have all the constructor's parameters we can create each of the    // dependency instances and then use the reflection instances to make a    // new instance of this class, injecting the created dependencies in.    // 解析出所有上下文依赖对象,带入函数,构造对象输出    $instances = $this ->resolveDependencies(      $dependencies    );    array_pop ( $this ->buildStack);    return $reflector ->newInstanceArgs( $instances ); }

总结

以上所述是小编给大家介绍的Laravel服务容器的绑定与解析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

原文链接:https://www.cnblogs.com/it-3327/archive/2019/11/04/11795559.html

可以去百度分享获取分享代码输入这里。
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

【腾讯云】云服务器产品特惠热卖中
搜索
标签列表
    关注我们

    了解等多精彩内容