前言老实说,第一天早上我的老板让我看laravel框架手册,我很绝望,因为我以前从未接触过它。说laravel的入门门槛确实有点高,但还是要硬着头皮看完(虽然我还有很多没看懂没用过)。后来根据公司项目的代码慢慢熟悉了laravel,但是还是停留在一些表面的功能上,比如依赖注入,ORM操作,用户认证,这些操作和我项目的业务逻辑相关,然后对于一些架构基础是的,比如服务提供者、服务容器、中间件、Redis等,这些都得从一开始就搭建好,但是我没有实际操作过(因为老大已经从一开始就做了),所以看说明书的时候还是有点迷糊。所以有空的时候,逛一下论坛,搜一下google,发现很多关于laravel的核心架构和网站使用方法的介绍(看了手册更容易理解),下面是基于一个我觉得不错的教学网站记录laravel核心架构学习网站地址:https://laraweb.net/这是一个日文网站,我觉得很适合新手,内容可以用浏览器翻译,毕竟日本人翻了个好懂的服务容器手册,介绍如下:Laravel服务容器是一个管理类依赖和进行依赖注入的工具。依赖注入是一个奇特的术语,本质上是指通过构造函数或在某些情况下通过“setter”方法将类的依赖项“注入”到类中。.....(我真的不明白什么意思)服务容器是用来管理类(服务)实例化的机制。直接看服务容器的使用方法1.在服务容器中注册类(bind)$this->app->bind('sender','MailSender');//$this->app成为服务容器。2.从服务容器中生成一个类(make)$sender=$this->app->make('sender');//从服务容器中创建一个sender类($this->app)。在这种情况下,将返回MailSender的一个实例。这是服务容器最简单的用法。下面是对服务容器的详细介绍(主要参考:https://www.cnblogs.com/lyzg/...)对laravel容器的基本认识一开始,index.php文件加载Composer生成定义好的自动加载器,然后从bootstrap/app.php脚本中检索Laravel应用程序的实例。Laravel本身采取的第一个动作是创建应用程序/服务容器的实例。$app=newIlluminate\Foundation\Application(目录名(__DIR__));每次请求到达laravel框架时都会执行这个文件,创建的$app是laravel框架的应用程序实例,只在整个请求生命周期中使用。Laravel提供了很多服务,包括认证、数据库、缓存、消息队列等。$app作为容器管理工具,负责几乎所有服务组件的实例化和实例的生命周期管理。当需要一个服务类来完成某个功能时,只需要通过容器解析一个该类型的实例即可。从最终的使用来看,laravel容器对服务实例的管理主要包括以下几个方面:服务绑定解析服务提供者管理别名的作用依赖注入先了解代码中如何获取容器实例,再学习上面的内容四个键以及如何在代码中获取容器实例。第一个是$app=app();//app这个辅助函数定义在\vendor\laravel\framework\src\Illuminate\Foundation\helper.php,,这个文件定义了很多帮助函数,会自动加载到通过作曲家项目。因此,任何参与http请求处理的代码位置都可以访问其中的函数,例如app()。第二个是Route::get('/',function(){dd(App::basePath());return'';});//这个其实用到了Facade,中文直译好像叫facade,在在config/app.php中有一段arrayaliases专门用来配置一些类型的别名。第一个是'App'=>Illuminate\Support\Facades\App::class。具体门面的细节请谷歌laravel。第三种实现方式是直接在服务提供者中使用$this->app。后面会介绍服务提供者,这里只介绍一下。因为服务提供者类都是由laravel容器实例化的,所以这些类继承自Illuminate\Support\ServiceProvider,它定义了一个实例属性$app:abstractclassServiceProvider{protected$app;laravel实例化服务提供者这时候,laravel容器实例就会被注入到这个$app中。所以在服务提供者中,我们始终可以通过$this->$app来访问laravel容器实例,而无需使用app()函数或AppFacade。如何理解服务绑定和解析从浅层意义上讲,容器既然是用来存储对象的,那么必然有一个对象存储和对象检索的过程。对象存储和对象检索的过程在Laravel中称为服务绑定和解析。app()->bind('service','thisisservice1');app()->bind('service2',['hi'=>function(){//sayhi}]);服务类{}app()->bind('service3',function(){returnnewService();});还有一种单例绑定singleton,是bind的特例(第三个参数为true),绑定到容器的对象只会被解析一次,后续调用都会返回同一个实例publicfunctionsingleton($abstract,$concrete=null){$this->bind($abstract,$concrete,true);}在绑定时,我们可以直接绑定初始化数据(基本类型、数组、对象实例),也可以使用匿名函数进行绑定。使用匿名函数的好处是服务绑定到容器后,不会立即生成服务的最终对象。只有当服务被解析时,才会执行匿名函数,此时会生成该服务对应的服务实例。其实我们在使用单例、bind方法、数组形式(这三种方法就是后面要介绍的绑定方式)进行服务绑定的时候,如果绑定的服务形式不是匿名函数,也会被包裹起来laravel中的匿名函数。这样无论绑定什么内容都可以实现上面介绍的惰性初始化功能,有利于容器的性能。这个从bind的源码可以看出一些细节:($abstract,$concrete=null,$shared=false)第一个参数是服务绑定的名称,第二个参数是服务绑定的结果(即闭包,获取实例),第三个参数表示服务是否在多个第一次解析时,总是返回第一次解析的实例(即单例绑定单例)。服务绑定也可以用数组的形式来完成:app()['service']=function(){returnnewService();};以上就是所有的绑定,接下来看分析,也就是拿出来用$service=app()->make('service');该方法接收两个参数,第一个是服务的绑定名和服务绑定名的别名,如果是别名,那么会根据服务绑定名的别名进行配置,找到最终的服务绑定名称,然后解析它;第二个参数是一个数组,最终将传递给服务绑定生成的闭包。查看源码:/***从容器中解析给定的类型。**@paramstring$abstract*@paramarray$parameters*@returnmixed*/publicfunctionmake($abstract,array$parameters=[]){return$this->resolve($abstract,$parameters);}/***从容器中解析给定的类型。**@paramstring$abstract*@paramarray$parameters*@returnmixed*/protectedfunctionresolve($abstract,$parameters=[]){$abstract=$this->getAlias($abstract);$needsContextualBuild=!空($参数)||!is_null($this->getContextualConcrete($abstract));//如果该类型的一个实例当前作为单例进行管理,我们将//只返回一个现有实例而不是实例化新实例//这样开发人员就可以每次都使用相同的对象实例。如果(isset($this->instances[$abstract])&&!$needsContextualBuild){return$this->instances[$abstract];}$this->with[]=$parameters;$concrete=$this->getConcrete($abstract);//我们准备实例化为绑定注册的具体类型的实例。这将实例化类型,并递归地解析任何//其“嵌套”依赖项,直到所有内容都已解析。如果($this->isBuildable($concrete,$abstract)){$object=$this->build($concrete);}else{$object=$this->make($concrete);}//如果我们为此类型定义了任何扩展器,我们将需要遍历它们//并将它们应用于正在构建的对象。//这允许扩展服务,例如更改配置或装饰对象。foreach($this->getExtenders($abstract)as$extender){$object=$extender($object,$this);}//如果请求的类型注册为单例,我们将缓存掉//“内存”中的实例,以便稍后返回无需在每个后续请求中创建对象的//全新实例。如果($this->isShared($abstract)&&!$needsContextualBuild){$this->instances[$abstract]=$object;}$this->fireResolvingCallbacks($abstract,$object);//在返回之前,我们还将已解决的标志设置为“true”并弹出//此构建的参数覆盖。完成这两件事后//我们将准备好返回完全构造的类实例。$this->resolved[$abstract]=true;array_pop($this->with);返回$object;}第1步:$needsContextualBuild=!empty($parameters)||!is_null($this->getContextualConcrete($abstract));该方法主要是区分解析出来的对象是否有参数。是依赖注入,所以传入的参数需要解析;这个后面第二步会分析:不需要上述参数依赖。我们可以直接返回$this->instances[$abstract]。第3步:$concrete=$this->getConcrete($abstract);.../***获取给定抽象的具体类型。**@paramstring$abstract*@returnmixed$concrete*/protectedfunctiongetConcrete($abstract){if(!is_null($concrete=$this->getContextualConcrete($abstract))){返回$concrete;}//如果我们没有针对该类型的注册解析器或具体内容,我们将只是//假定每个类型都是具体名称并尝试按原样解析它//因为容器应该能够解析具体内容自动地。如果(isset($this->bindings[$abstract])){return$this->bindings[$abstract]['concrete'];}return$abstract;}这一步主要是先找到绑定上下文,是否能找到绑定类;如果没有,则从$bindings[]中找到关联的实现类;如果最后没有找到,就直接返回$abstract本身。//我们已准备好实例化为//绑定注册的具体类型的实例。这将实例化类型,并以递归方式解析任何//它的“嵌套”依赖项,直到所有内容都已解析。if($this->isBuildable($concrete,$abstract)){$object=$this->build($concrete);}else{$object=$this->make($concrete);}.../***确定给定的混凝土是否可建造。**@parammixed$concrete*@paramstring$abstract*@returnbool*/protectedfunctionisBuildable($concrete,$abstract){返回$concrete===$abstract||$concreteinstanceofClosure;}如果之前找到的$concrete返回了一个$abstract值,或者$concrete是一个闭包,则执行$this->build($concrete),否则说明存在嵌套依赖,则使用recursive方法执行$this->make($concrete)直到解析完所有内容。$this->build($concrete)/***实例化给定类型的具体实例。**@paramstring$concrete*@returnmixed**@throws\Illuminate\Contracts\Container\BindingResolutionException*/publicfunctionbuild($concrete){//如果具体类型实际上是一个闭包,我们将直接执行它并且//交回函数的结果,这允许函数//用作解析器,以便对这些对象进行更精细的解析。//如果传入闭包,则直接执行闭包函数并返回结果if($concreteinstanceofClosure){return$concrete($this,$this->getLastParameterOverride());}//使用反射机制来解析这个类。$reflector=newReflectionClass($concrete);//如果类型不可实例化,则开发人员正在尝试解析//抽象类型,例如抽象类的接口,并且//没有为抽象注册绑定,因此我们需要退出。如果(!$reflector->isInstantiable()){返回$this->notInstantiable($concrete);}$this->buildStack[]=$concrete;//获取构造函数$constructor=$reflector->getConstructor();//如果没有构造函数,则意味着没有依赖关系//我们可以立即解析对象的实例,而无需//从这些容器中解析任何其他类型或依赖关系。//如果没有构造函数,则表示没有传入任何参数,也就是说不需要进行相应的上下文相关的解析。if(is_null($constructor)){//Pop构建过程的内容,然后直接构建对象输出。array_pop($this->buildStack);返回新的$混凝土;}//获取构造函数的参数$dependencies=$constructor->getParameters();//一旦我们拥有了所有构造函数的参数,我们就可以创建每个//依赖实例,然后使用反射实例来创建//这个类的新实例,将创建的依赖注入。//解析出所有上下文相关的对象,将它们带入函数,构造对象输出$instances=$this->resolveDependencies($dependencies);array_pop($this->buildStack);return$reflector->newInstanceArgs($instances);}上面这段关于解析make的内容主要参考:coding01:看Laravel源码了解Container这篇文章主要是学习Laravel的服务容器及其绑定和解析,虽然我可以目前还看不懂框架源码的每一部分,但是通过这些优秀的文章,我会进行整理和组合。这个过程让我更加了解laravel。一些核心的内容,至少别人问的时候我能说多说少,这就是进步。后面关于服务提供者、依赖注入、中间件等的学习会放在后续的博文中。欢迎阅读我的其他博文:https://zgxxx.github.io/。以上相关知识已注明出处,如有侵权请联系本人,感谢这些优秀文章的作者
