搜尋
首頁php框架Laravel關於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(https://juejin.cn/post/6844903608052367368)

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

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

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

陳述
本文轉載於:segmentfault。如有侵權,請聯絡admin@php.cn刪除
將JavaScript框架(React,Vue,Angular)與Laravel Backend集成將JavaScript框架(React,Vue,Angular)與Laravel Backend集成May 03, 2025 am 12:20 AM

React,vue,andangularCanBeintegratedWithLaravelByFollowingSpecificetUpSteps.1)forreact:installReactusingLaravelui,setUpComponentsInapp.js.js.js.js.2)forvue:uselaravel'sbuilt'sbuilt'sbuilt invuesupport,configureinapp.js.3)forangular forangular setuplare setulare sereptery sereptry and parkuly sereftery secparroughtery secparroughtery secparroughtery

任務管理工具:遠程項目的優先級和跟踪進度任務管理工具:遠程項目的優先級和跟踪進度May 02, 2025 am 12:25 AM

taskManagementsToolSareEssentialForefectiverMototeprojectManagementbyPrioritizingTaskSandTrackingProgress.1)USETOOLSLIKETRELLOANDASANATASANATOSETPRIORITIONTAGS.2)

最新的Laravel版本如何提高性能?最新的Laravel版本如何提高性能?May 02, 2025 am 12:24 AM

Laravel10enhancesPerformancEthroughSeveralKeyKeyFeatures.1)itintroducesquereBuilderCachingTordorcachingTordOuctedSataBaseload.2)itoptimiesseloizeseloquentmodelloAdingwithlazyproxies.3)

全棧Laravel應用程序的部署策略全棧Laravel應用程序的部署策略May 02, 2025 am 12:22 AM

最佳的全棧Laravel應用部署策略包括:1.零停機部署,2.藍綠部署,3.持續部署,4.金絲雀發布。 1.零停機部署使用Envoy或Deployer自動化部署過程,確保應用在更新時保持可用。 2.藍綠部署通過維護兩個環境實現無停機部署,並允許快速回滾。 3.持續部署通過GitHubActions或GitLabCI/CD自動化整個部署流程。 4.金絲雀發布通過Nginx配置,將新版本逐步推廣給用戶,確保性能優化和快速回滾。

擴展全堆棧Laravel應用程序:最佳實踐和技術擴展全堆棧Laravel應用程序:最佳實踐和技術May 02, 2025 am 12:22 AM

toscalealaravelApplication有效,焦點databaseSharding,緩存,負載平衡和microservices.1)實現DataBasEshardingTodistaCripedataCrossmultipledataBasesForimProvesforimPrevperformance.2)uselaravel'scachingsystemystemystemystemywithredsormememememememcachedtebachedtebab

沉默的鬥爭:克服分佈式團隊中的溝通障礙沉默的鬥爭:克服分佈式團隊中的溝通障礙May 02, 2025 am 12:20 AM

doovercomecommunicationbarriersIndistributedTeams,使用:1)VideoCallSforface-to-Faceinteraction,2)setClearresponsEtimepections,3)chooseappropropropraproproprapropropriatecommunicationTools,4)CreatseateAteAteAteamCommunicationGuide和5)建立PemersonalBoundariestAriestOpeopReventBreventBurniationBurnication.the

使用Laravel Blade在全棧項目中進行前端模板使用Laravel Blade在全棧項目中進行前端模板May 01, 2025 am 12:24 AM

laravelbladeenhancesfrontendtemplatinginflatinginflationll-stackprojectsbyferingCleanSyntaxandaxandpoperfelfulfeatures.1)itallowsforeasyvariableasyvariabledisplayandControlstructures.2)bladesuportsuportsuportscreatingingingingingingingingingingangingandredreingscomponents components components components,aidinginmanagingcomplexuis.3)

使用Laravel:實用教程構建全堆棧應用程序使用Laravel:實用教程構建全堆棧應用程序May 01, 2025 am 12:23 AM

laravelisidealforll-stackapplicationsduetoitselegantsyntax,complastissionecosystem和perperatedfulfeatures.1)useeloquentormforintuivelbackenddatamanipulation,butavoidn 1queryissues.2)

See all articles

熱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

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

熱工具

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。