目錄
前言
關於服務容器
laravel容器基本認識
如何在程式碼中取得到容器實例
如何理解服務綁定與解析
首頁 php框架 Laravel Laravel:服務容器綁定與解析

Laravel:服務容器綁定與解析

Apr 22, 2021 am 11:33 AM
laravel php

以下由laravel教學專欄為大家介紹Laravel:服務容器綁定與解析,希望對需要的朋友有幫助!

Laravel修練:服務容器綁定與解析

前言

  老實說,第一次老大讓我看laravel框架手冊的那天早上,我是很絕望的,因為真的沒接觸過,對我這種渣渣來說,laravel的入門門檻確實有點高了,但還是得硬著頭皮看下去(雖然到現在我還有很多沒看懂,也沒用過)。
  後面慢慢根據公司專案的程式碼對laravel也慢慢​​熟悉起來了,但還是停留在一些表面的功能,例如依賴注入,ORM操作,用戶認證這些和我專案業務邏輯相關的操作,然後對於一些架構基礎的,例如服務提供器,服務容器,中間件,Redis等這些一開始就要設定好的東西,我倒是沒實際操作過(因為老大一開始就做好了),所以看手冊還是有點懵。
  所以有空的時候逛逛論壇,搜下Google就發現許多關於laravel核心架構的介紹,以及如何使用的網站(確實看完後再去看手冊就好理解多了),下面就根據一個我覺得不錯的網站上面的教學來記錄一下laravel核心架構的學習
網站地址:https://laraweb.net/ 這是一個日本的網站,我覺得挺適合新手的,內容用瀏覽器翻譯過來就ok了,畢竟日文直翻過來很好理解的

關於服務容器

  手冊上是這樣介紹的:Laravel 服務容器是用於管理類別的依賴和執行依賴注入的工具。依賴注入這個花俏名詞實質上是指:類別的依賴項透過建構函數,或是某些情況下透過「setter」方法「注入」到類別中。 。 。 。 。 。 (真的看不懂啥意思)
  服務容器是用來管理類別(服務)的實例化的機制。直接看看服務容器怎麼用

  1.在服務容器中註冊類別(bind)

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

  2.從服務容器產生類別(make)

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

  這是服務容器最簡單的使用,以下是服務容器的詳細介紹
(主要參考:https://www.cnblogs.com/lyzg/...)

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);
}
登入後複製

上面这一段有关解析make的介绍主要参考:
coding01:看 Laravel 源代码了解 Container

  这一篇就主要学习laravel的服务容器以及它的绑定和解析,虽然目前能力无法对框架源码每一个地方都弄懂,但通过这几篇优秀的文章,我将其进行整理结合,这过程让我更加理解laravel的一些核心内容,起码别人问起来我多多少少能说出一些,这就是进步。

  后面有关服务提供者,依赖注入,中间件等内容的学习将放在后续的博客文章中,欢迎看看我的其他博客文章:https://zgxxx.github.io/。
  以上相关知识的引用已经注明出处,若有侵权,请联系我,感谢这些优秀文章的作者

以上是Laravel:服務容器綁定與解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1321
25
PHP教程
1269
29
C# 教程
1249
24
繼續使用PHP:耐力的原因 繼續使用PHP:耐力的原因 Apr 19, 2025 am 12:23 AM

PHP仍然流行的原因是其易用性、靈活性和強大的生態系統。 1)易用性和簡單語法使其成為初學者的首選。 2)與web開發緊密結合,處理HTTP請求和數據庫交互出色。 3)龐大的生態系統提供了豐富的工具和庫。 4)活躍的社區和開源性質使其適應新需求和技術趨勢。

IIS和PHP的兼容性:深度潛水 IIS和PHP的兼容性:深度潛水 Apr 22, 2025 am 12:01 AM

IIS和PHP可以兼容,通過FastCGI實現。 1.IIS通過配置文件將.php文件請求轉發給FastCGI模塊。 2.FastCGI模塊啟動PHP進程處理請求,提高性能和穩定性。 3.實際應用中需注意配置細節、錯誤調試和性能優化。

Laravel vs. Python(與框架):比較分析 Laravel vs. Python(與框架):比較分析 Apr 21, 2025 am 12:15 AM

Laravel適合團隊熟悉PHP且需功能豐富的項目,Python框架則視項目需求而定。 1.Laravel提供優雅語法和豐富功能,適合需要快速開發和靈活性的項目。 2.Django適合複雜應用,因其“電池包含”理念。 3.Flask適用於快速原型和小型項目,提供極大靈活性。

如果session_start()被多次調用會發生什麼? 如果session_start()被多次調用會發生什麼? Apr 25, 2025 am 12:06 AM

多次調用session_start()會導致警告信息和可能的數據覆蓋。 1)PHP會發出警告,提示session已啟動。 2)可能導致session數據意外覆蓋。 3)使用session_status()檢查session狀態,避免重複調用。

使用Laravel:使用PHP簡化Web開發 使用Laravel:使用PHP簡化Web開發 Apr 19, 2025 am 12:18 AM

Laravel優化Web開發流程的方法包括:1.使用路由系統管理URL結構;2.利用Blade模板引擎簡化視圖開發;3.通過隊列處理耗時任務;4.使用EloquentORM簡化數據庫操作;5.遵循最佳實踐提高代碼質量和可維護性。

作曲家:通過AI的幫助開發PHP 作曲家:通過AI的幫助開發PHP Apr 29, 2025 am 12:27 AM

AI可以幫助優化Composer的使用,具體方法包括:1.依賴管理優化:AI分析依賴關係,建議最佳版本組合,減少衝突。 2.自動化代碼生成:AI生成符合最佳實踐的composer.json文件。 3.代碼質量提升:AI檢測潛在問題,提供優化建議,提高代碼質量。這些方法通過機器學習和自然語言處理技術實現,幫助開發者提高效率和代碼質量。

哪些數據庫版本與最新的Laravel兼容? 哪些數據庫版本與最新的Laravel兼容? Apr 25, 2025 am 12:25 AM

最新版本的Laravel10與MySQL5.7及以上、PostgreSQL9.6及以上、SQLite3.8.8及以上、SQLServer2017及以上兼容。這些版本選擇是因為它們支持Laravel的ORM功能,如MySQL5.7的JSON數據類型,提升了查詢和存儲效率。

php框架laravel和yii區別是什麼 php框架laravel和yii區別是什麼 Apr 30, 2025 pm 02:24 PM

Laravel和Yii的主要區別在於設計理念、功能特性和使用場景。 1.Laravel注重開發的簡潔和愉悅,提供豐富的功能如EloquentORM和Artisan工具,適合快速開發和初學者。 2.Yii強調性能和效率,適用於高負載應用,提供高效的ActiveRecord和緩存系統,但學習曲線較陡。

See all articles