PHPer:Laravel框架学习服务容器绑定与解析

在这里插入图片描述
1.在服务容器中注册类(bind)

t h i s > a p p > b i n d ( s e n d e r , M a i l S e n d e r ) ; / / this->app->bind('sender','MailSender');// this->app成为服务容器。

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

$sender = t h i s > a p p > m a k e ( s e n d e r ) ; / / this->app->make('sender');//从服务容器( this->app)创建一个sender类。在这种情况下,将返回MailSender的实例。
这是服务容器最简单的使用,下面是对服务容器的详细介绍

laravel容器基本认识

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

KaTeX parse error: Expected group after '_' at position 53: …tion( dirname(_̲_DIR__) ); 这个文件…app即是laravel框架的应用程序实例,它在整个请求生命周期都是唯一的。laravel提供了很多服务,包括认证,数据库,缓存,消息队列等等,$app作为一个容器管理工具,负责几乎所有服务组件的实例化以及实例的生命周期管理。当需要一个服务类来完成某个功能的时候,仅需要通过容器解析出该类型的一个实例即可。从最终的使用方式来看,laravel容器对服务实例的管理主要包括以下几个方面:

服务的绑定与解析
服务提供者的管理
别名的作用
依赖注入
先了解如何在代码中获取到容器实例,再学习上面四个关键

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

第一种是

$app = app();//app这个辅助函数定义在 endorlaravel rameworksrcIlluminateFoundationhelper.php里面,,这个文件定义了很多help函数,并且会通过composer自动加载到项目中。
所以,在参与http请求处理的任何代码位置都能够访问其中的函数,比如app()。
第二种是

扫描二维码关注公众号,回复: 4092218 查看本文章

Route::get(’/’, function () {
dd(App::basePath()); return ‘’;
});//这个其实是用到Facade,中文直译貌似叫门面,在config/app.php中,有一节数组aliases专门用来配置一些类型的别名,第一个就是’App’ => IlluminateSupportFacadesApp::class,
具体的Google一下laravel有关门面的具体实现方式
第三种是

在服务提供者里面直接使用 t h i s > a p p l a r a v e l I l l u m i n a t e S u p p o r t S e r v i c e P r o v i d e r this->app。服务提供者后面还会介绍,现在只是引入。因为服务提供者类都是由laravel容器实例化的,这些类都继承自IlluminateSupportServiceProvider,它定义了一个实例属性 app:

abstract class ServiceProvider{ protected a p p ; l a r a v e l l a r a v e l app; laravel在实例化服务提供者的时候,会把laravel容器实例注入到这个 app上面。所以我们在服务提供者里面,始终能通过 t h i s > this-> app访问到laravel容器实例,而不需要再使用app()函数或者App Facade了。

如何理解服务绑定与解析

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

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),绑定到容器的对象只会被解析一次,之后的调用都返回相同的实例

public function singleton($abstract, KaTeX parse error: Expected '}', got 'EOF' at end of input: …ncrete = null){this->bind($abstract, $concrete, true);
}
在绑定的时候,我们可以直接绑定已经初始化好的数据(基本类型、数组、对象实例),还可以用匿名函数来绑定。用匿名函数的好处在于,这个服务绑定到容器以后,并不会立即产生服务最终的对象,只有在这个服务解析的时候,匿名函数才会执行,此时才会产生这个服务对应的服务实例。

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

if (! $concrete instanceof Closure) {
$concrete = t h i s > g e t C l o s u r e ( this->getClosure( abstract, $concrete);
}
看看bind的底层代码

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

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

app()[‘service’] = function(){ return new Service();
};
绑定大概就这些,接下来看解析,也就是取出来用

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

看源码:

/**

  • Resolve the given type from the container.
  • @param string $abstract
  • @param array $parameters
  • @return mixed
    /public function make($abstract, array $parameters = []){ return t h i s > r e s o l v e ( 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 = t h i s > g e t A l i a s ( this->getAlias( abstract);
    n e e d s C o n t e x t u a l B u i l d = ! e m p t y ( needsContextualBuild = ! empty( parameters) || ! is_null( t h i s > g e t C o n t e x t u a l C o n c r e t e ( 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( t h i s > i n s t a n c e s [ this->instances[ abstract]) && ! $needsContextualBuild) { return t h i s > i n s t a n c e s [ this->instances[ abstract];
    } $this->with[] = $parameters;
    $concrete = t h i s > g e t C o n c r e t e ( 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 ( t h i s > i s B u i l d a b l e ( this->isBuildable( concrete, $abstract)) {
    $object = t h i s > b u i l d ( this->build( concrete);
    } else {
    $object = t h i s > m a k e ( 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 ( t h i s > g e t E x t e n d e r s ( this->getExtenders( abstract) as $extender) {
    $object = e x t e n d e r ( extender( object, KaTeX parse error: Expected 'EOF', got '}' at position 8: this); }̲ // If the requ…this->isShared($abstract) && ! $needsContextualBuild) { t h i s > i n s t a n c e s [ this->instances[ abstract] = $object;
    } t h i s > f i r e R e s o l v i n g C a l l b a c k s ( 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.
    t h i s > r e s o l v e d [ this->resolved[ abstract] = true;
    array_pop($this->with); return $object;
    }
    第一步:

n e e d s C o n t e x t u a l B u i l d = ! e m p t y ( needsContextualBuild = ! empty( parameters) || ! is_null( t h i s > g e t C o n t e x t u a l C o n c r e t e ( this->getContextualConcrete( abstract)
);
该方法主要是区分,解析的对象是否有参数,如果有参数,还需要对参数做进一步的分析,因为传入的参数,也可能是依赖注入的,所以还需要对传入的参数进行解析;这个后面再分析。

第二步:

if (isset( t h i s > i n s t a n c e s [ this->instances[ abstract]) && ! $needsContextualBuild) { return t h i s > i n s t a n c e s [ this->instances[ abstract];
}
如果是绑定的单例,并且不需要上面的参数依赖。我们就可以直接返回 t h i s > i n s t a n c e s [ this->instances[ abstract]。

第三步:

$concrete = t h i s > g e t C o n c r e t e ( this->getConcrete( abstract);
…/**

  • Get the concrete type for a given abstract.
  • @param string $abstract
  • @return mixed c o n c r e t e / p r o t e c t e d f u n c t i o n g e t C o n c r e t e ( concrete */protected function getConcrete( abstract){ if (! is_null($concrete = t h i s > g e t C o n t e x t u a l C o n c r e t e ( this->getContextualConcrete( abstract))) { return KaTeX parse error: Expected 'EOF', got '}' at position 11: concrete; }̲ // If we don't…this->bindings[$abstract])) { return t h i s > b i n d i n g s [ this->bindings[ abstract][‘concrete’];
    } return $abstract;
    }
    这一步主要是先从绑定的上下文找,是不是可以找到绑定类;如果没有,则再从 $bindings[] 中找关联的实现类;最后还没有找到的话,就直接返回 $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 ( t h i s > i s B u i l d a b l e ( this->isBuildable( concrete, $abstract)) {
$object = t h i s > b u i l d ( this->build( concrete);
} else {
$object = t h i s > m a k e ( 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 是个闭包,则执行 t h i s > b u i l d ( this->build( concrete),否则,表示存在嵌套依赖的情况,则采用递归的方法执行 t h i s > m a k e ( this->make( concrete),直到所有的都解析完为止。

t h i s > b u i l d ( this->build( concrete)

/**

  • Instantiate a concrete instance of the given type.
  • @param string $concrete
  • @return mixed
  • @throws IlluminateContractsContainerBindingResolutionException
    */public function build(KaTeX parse error: Expected '}', got 'EOF' at end of input: …行闭包函数,返回结果 if (concrete instanceof Closure) { return c o n c r e t e ( concrete( this, $this->getLastParameterOverride());
    } // 利用反射机制,解析该类。
    r e f l e c t o r = n e w R e f l e c t i o n C l a s s ( 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 t h i s > n o t I n s t a n t i a b l e ( this->notInstantiable( concrete);
    } $this->buildStack[] = $concrete; // 获取构造函数
    $constructor = r e f l e c t o r > g e t C o n s t r u c t o r ( ) ; / / I f t h e r e a r e n o c o n s t r u c t o r s , t h a t m e a n s t h e r e a r e n o d e p e n d e n c i e s t h e n / / w e c a n j u s t r e s o l v e t h e i n s t a n c e s o f t h e o b j e c t s r i g h t a w a y , w i t h o u t / / r e s o l v i n g a n y o t h e r t y p e s o r d e p e n d e n c i e s o u t o f t h e s e c o n t a i n e r s . / / i f ( i s n u l l ( 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(
    d e p e n d e n c i e s ) ; a r r a y p o p ( dependencies ); array_pop( this->buildStack); return r e f l e c t o r > n e w I n s t a n c e A r g s ( reflector->newInstanceArgs( instances);
    }
    在这里插入图片描述
    在这里插入图片描述
    这一篇就主要学习laravel的服务容器以及它的绑定和解析,框架源码分析核心内容请期待后面的文章,欢迎拍砖。

猜你喜欢

转载自blog.csdn.net/weixin_42953849/article/details/83378969
今日推荐