Heim > Artikel > PHP-Framework > Bindung und Analyse des Laravel-Servicecontainers
Der Inhalt dieses Artikels befasst sich mit dem Binden und Parsen von Laravel-Servicecontainern. Ich hoffe, dass er für Freunde in Not hilfreich ist.
Vorwort
Ehrlich gesagt war ich am ersten Morgen, als der Chef mich bat, das Laravel-Framework-Handbuch zu lesen, sehr verzweifelt, weil ich wirklich keinen Kontakt damit hatte , oder? Für einen Drecksack wie mich ist die Eintrittsbarriere für Laravel zwar etwas hoch, aber ich muss trotzdem in den sauren Apfel beißen und weiterlesen (obwohl ich immer noch nicht viel verstehe und es noch nicht genutzt habe).
Später wurde ich anhand des Projektcodes des Unternehmens allmählich mit Laravel vertraut, blieb jedoch bei einigen oberflächlichen Funktionen wie Abhängigkeitsinjektion, ORM-Operationen, Benutzerauthentifizierung und anderen Vorgängen im Zusammenhang mit der Geschäftslogik meines Projekts für Einige grundlegende Architekturen wie Dienstanbieter, Dienstcontainer, Middleware, Redis usw., die von Anfang an eingerichtet werden müssen, wurden von mir nicht tatsächlich betrieben (weil der Chef dies bereits von Anfang an getan hat). Daher ist es immer noch etwas verwirrend, das Handbuch zu lesen.
Wenn ich also Freizeit habe, durchsuche ich die Foren und durchsuche Google, um viele Einführungen in die Kernarchitektur von Laravel und deren Verwendung zu finden (es wird einfacher zu verstehen sein, wenn man das Handbuch nach dem Lesen hier liest). ist ein Leitfaden, der meiner Meinung nach gut ist, um das Erlernen der Laravel-Kernarchitektur aufzuzeichnen Der Inhalt wird mit dem Browser übersetzt. Es ist in Ordnung, schließlich ist er leicht zu verstehen, wenn Sie den japanischen Text übersetzen.
Über den Service-Container
Das Handbuch stellt es wie folgt vor Dies: Der Laravel-Servicecontainer wird für Verwaltungsklassenabhängigkeiten und Tools zur Durchführung der Abhängigkeitsinjektion verwendet. Der ausgefallene Begriff „Abhängigkeitsinjektion“ bedeutet im Wesentlichen, dass die Abhängigkeiten einer Klasse über den Konstruktor oder in einigen Fällen über die „Setter“-Methode in die Klasse „injiziert“ werden. . . . . . (Ich verstehe wirklich nicht, was es bedeutet) Der Service-Container ist ein Mechanismus, der zum Verwalten der Instanziierung von Klassen (Diensten) verwendet wird. Sehen Sie direkt, wie Sie den Service-Container verwenden
$this->app->bind('sender','MailSender'); //$this->app成为服务容器。2. Generieren Sie die Klasse aus dem Service-Container (make)
$sender = $this->app->make('sender'); //从服务容器($this->app)创建一个sender类。 在这种情况下,将返回MailSender的实例。Dies ist die einfachste Verwendung von Service-Containern. Im Folgenden finden Sie eine detaillierte Einführung in Service-Container
Grundlegendes Verständnis von Laravel-Containern
Zu Beginn Die Datei index.php wird vom Composer geladen und generiert. Der definierte Autoloader ruft dann eine Instanz der Laravel-Anwendung aus dem Skript bootstrap/app.php ab. Die erste Aktion, die Laravel selbst durchführt, besteht darin, eine Instanz des Anwendungs-/Dienstcontainers zu erstellen.$app = new Illuminate\Foundation\Application( dirname(__DIR__) );Diese Datei wird jedes Mal ausgeführt, wenn eine Anfrage das Laravel-Framework erreicht. Die erstellte $app ist die Anwendungsinstanz des Laravel-Frameworks, die während des gesamten Anfragelebenszyklus eindeutig ist. Laravel bietet viele Dienste, darunter Authentifizierung, Datenbank, Cache, Nachrichtenwarteschlange usw. $app ist als Container-Management-Tool für die Instanziierung fast aller Servicekomponenten und das Lebenszyklusmanagement der Instanz verantwortlich. Wenn eine Serviceklasse zum Abschließen einer bestimmten Funktion benötigt wird, muss sie lediglich eine Instanz dieses Typs über den Container auflösen. Aus Sicht der endgültigen Verwendung umfasst die Verwaltung von Dienstinstanzen durch den Laravel-Container hauptsächlich die folgenden Aspekte:
Dienstbindung und -analyse
Management von Dienstleistern
Die Rolle von Aliasnamen
Abhängigkeitsinjektion
$app = app(); //app这个辅助函数定义在\vendor\laravel\framework\src\Illuminate\Foundation\helper.php 里面,,这个文件定义了很多help函数,并且会通过composer自动加载到项目中。 所以,在参与http请求处理的任何代码位置都能够访问其中的函数,比如app()。die zweite ist
Route::get('/', function () { dd(App::basePath()); return ''; }); //这个其实是用到Facade,中文直译貌似叫门面,在config/app.php中, 有一节数组aliases专门用来配置一些类型的别名,第一个就是'App' => Illuminate\Support\Facades\App::class, 具体的Google一下laravel有关门面的具体实现方式die dritte ist Verwenden Sie $this direkt im Dienstanbieter -> App. Der Dienstanbieter wird später vorgestellt, er wird jedoch erst jetzt eingeführt. Da Dienstanbieterklassen vom Laravel-Container instanziiert werden, erben diese Klassen von IlluminateSupportServiceProvider, das ein Instanzattribut $app definiert:
abstract class ServiceProvider { protected $app;Wenn Laravel den Dienstanbieter instanziiert, wird Laravel die Containerinstanz in diese $app injizieren . Beim Dienstanbieter können wir also jederzeit über $this->$app auf die Laravel-Container-Instanz zugreifen, ohne die Funktion app() oder App Facade zu verwenden. So verstehen Sie die Bindung und Analyse von Diensten Da der Container zum Speichern von Objekten verwendet wird, muss im einfachen Sinne ein Prozess zum Speichern und Entfernen von Objekten vorhanden sein. Der Prozess des Speicherns und Abrufens dieses Objekts wird in Laravel als Service-Bindung und -Analyse bezeichnet.
app()->bind('service', 'this is service1'); app()->bind('service2', [ 'hi' => function(){ //say hi } ]); class Service { } app()->bind('service3', function(){ return new Service(); }); Es gibt auch einen Singleton-Bindungs-Singleton, der ein Sonderfall von bind ist (der dritte Parameter ist wahr). Das an den Container gebundene Objekt wird nur einmal analysiert und nachfolgende Aufrufe geben dasselbe zurück Beispiel für
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); }
Das obige ist der detaillierte Inhalt vonBindung und Analyse des Laravel-Servicecontainers. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!