laravel学习:容器绑定与解析

时间:2022-09-21 14:51:52

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

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

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

$sender = $this->app->make('sender');//从服务容器($this->app)创建一个sender类。在这种情况下,将返回MailSender的实例。

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

laravel容器基本认识

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

$app = new Illuminate\Foundation\Application(
   dirname(__DIR__)
);

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

  • 服务的绑定与解析

  • 服务提供者的管理

  • 别名的作用

  • 依赖注入

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

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

第一种是

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

第二种是

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:

abstract class ServiceProvider{    protected $app;

  laravel在实例化服务提供者的时候,会把laravel容器实例注入到这个$app上面。所以我们在服务提供者里面,始终能通过$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, $concrete = null){$this->bind($abstract, $concrete, true);
}

  在绑定的时候,我们可以直接绑定已经初始化好的数据(基本类型、数组、对象实例),还可以用匿名函数来绑定。用匿名函数的好处在于,这个服务绑定到容器以后,并不会立即产生服务最终的对象,只有在这个服务解析的时候,匿名函数才会执行,此时才会产生这个服务对应的服务实例。
  实际上,当我们使用singleton,bind方法以及数组形式,(这三个方法是后面要介绍的绑定的方法),进行服务绑定的时候,如果绑定的服务形式,不是一个匿名函数,也会在laravel内部用一个匿名函数包装起来,这样的话, 不轮绑定什么内容,都能做到前面介绍的懒初始化的功能,这对于容器的性能是有好处的。这个可以从bind的源码中看到一些细节:

if (! $concrete instanceof Closure) {
   $concrete = $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 $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;
}

第一步:

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

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

第二步:

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

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

第三步:

$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 本身。

// 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),直到所有的都解析完为止。

$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学习:容器绑定与解析的更多相关文章

  1. Laravel修炼:服务容器绑定与解析

    前言   老实说,第一次老大让我看laravel框架手册的那天早上,我是很绝望的,因为真的没接触过,对我这种渣渣来说,laravel的入门门槛确实有点高了,但还是得硬着头皮看下去(虽然到现在我还有很多 ...

  2. Laravel服务容器的绑定与解析

    本篇文章给大家带来的内容是关于Laravel服务容器的绑定与解析,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 前言   老实说,第一次老大让我看laravel框架手册的那天早上,我 ...

  3. Laravel开发:Laravel核心——Ioc服务容器源码解析(服务器解析)

    make解析 服务容器对对象的自动解析是服务容器的核心功能,make 函数.build 函数是实例化对象重要的核心,先大致看一下代码: public function make($abstract) ...

  4. Laravel 学习笔记 —— 神奇的服务容器 [转]

    容器,字面上理解就是装东西的东西.常见的变量.对象属性等都可以算是容器.一个容器能够装什么,全部取决于你对该容器的定义.当然,有这样一种容器,它存放的不是文本.数值,而是对象.对象的描述(类.接口)或 ...

  5. laravel 学习笔记 —— 神奇的服务容器

    转载自:https://www.insp.top/learn-laravel-container 容器,字面上理解就是装东西的东西.常见的变量.对象属性等都可以算是容器.一个容器能够装什么,全部取决于 ...

  6. laravel 学习笔记 — 神奇的服务容器

     2015-05-05 14:24 来自于分类 笔记 Laravel PHP开发 竟然有人认为我是抄 Laravel 学院的,心塞.世界观已崩塌. 容器,字面上理解就是装东西的东西.常见的变量.对象属 ...

  7. Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md

    写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...

  8. Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签

    写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...

  9. Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作

    写在前面 上文 Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件主要讲Spring容器创建时通过XmlBeanDefinitionReader读 ...

随机推荐

  1. JAVA WEB 的JSP(9*9乘法表+*型金字塔)

    运行环境及工具: (Tomcat7) + (JAVA JDK)+ (Eclipse for J2EE) 输出9*9乘法表 代码片段的练习 增加一些简单的JS功能 <%@ page import= ...

  2. start from here

    简单介绍下,小码农一只,工作两年左右,从事移动端游戏开发工作. 人类,精神正常,男,爱好游戏音乐足球美女,if(有妹子) 喜欢旅游;,取向正常. 很喜欢java,喜欢关注新技术, 应一些朋友的建议,今 ...

  3. LINUX 下mysql数据库导出

    mysqldump -u root -p dbname > db.sql

  4. jQueryMobile之listview

    效果如下: data-inset="true":为圆角 data-filter="true":带着过滤输入框 <!DOCTYPE html> &lt ...

  5. andoid x项目的优化 1

    通常我们写程序,都是在项目计划的压力下完成的,此时完成的代码可以完成具体业务逻辑,但是性能不一定是最优化的,一般来说,一般来说,优秀的程序员在写完代码之后都会不断的对代码进行重构.重构的好处有很多,其 ...

  6. fresco中设置占位&sol;加载失败的图片 无效

    在xml中设置  placeholderImage  属性无效.代码如下: <com.facebook.drawee.view.SimpleDraweeView android:id=&quot ...

  7. J2EE 与 Java EE

    J2EE(Java 2 Enterprise Edition)和Java EE是一样的,由于J2EE的名称容易引起误解,Sun将J2EE更名为Java EE. 2005年6月,JavaOne大会召开, ...

  8. Eclipse注释配置

    新的文件/** * @ClassName: ${type_name}  * @Description: ${todo} * @author ${user} * @date ${date} ${time ...

  9. 现代 JavaScript 框架存在的主要原因

    简评:现代 JavaScript 框架的出现最主要是解决哪个问题?这篇文章很好的解释了这个问题. 我见过许多人盲目地使用像 React,Angular 或 Vue.js 这样的现代框架.这些框架提供了 ...

  10. lol人物模型提取(二)

      两个dds文件怎么导入到一个模型上呢?这模型又不能拆开.   一开始我想的是用两个材质球来完成,一个材质球对应一个dds文件,然而行不通.   一个材质球对应两个dds文件还不太会弄,于是我想着干 ...