PHPer:Laravel框架學習服務容器繫結與解析
1.在服務容器中註冊類(bind)
this->app成為服務容器。
2.從服務容器生成類(make)
$sender = this->app)建立一個sender類。在這種情況下,將返回MailSender的例項。 這是服務容器最簡單的使用,下面是對服務容器的詳細介紹
laravel容器基本認識
一開始,index.php 檔案載入 Composer 生成定義的自動載入器,然後從 bootstrap/app.php 指令碼中檢索 Laravel 應用程式的例項。Laravel 本身採取的第一個動作是建立一個 application/ service container 的例項。
KaTeX parse error: Expected group after '_' at position 53: …tion( dirname(_̲_DIR__) ); 這個檔案…
服務的繫結與解析 服務提供者的管理 別名的作用 依賴注入 先了解如何在程式碼中獲取到容器例項,再學習上面四個關鍵
如何在程式碼中獲取到容器例項
第一種是
$app = app();//app這個輔助函式定義在endorlaravelrameworksrcIlluminateFoundationhelper.php裡面,,這個檔案定義了很多help函式,並且會通過composer自動載入到專案中。 所以,在參與http請求處理的任何程式碼位置都能夠訪問其中的函式,比如app()。 第二種是
Route::get(’/’, function () { dd(App::basePath()); return ‘’; });//這個其實是用到Facade,中文直譯貌似叫門面,在config/app.php中,有一節陣列aliases專門用來配置一些型別的別名,第一個就是’App’ => IlluminateSupportFacadesApp::class, 具體的Google一下laravel有關門面的具體實現方式 第三種是
在服務提供者裡面直接使用app:
abstract class ServiceProvider{ protected app上面。所以我們在服務提供者裡面,始終能通過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, KaTeX parse error: Expected '}', got 'EOF' at end of input: …ncrete = null){this->bind($abstract, $concrete, true); } 在繫結的時候,我們可以直接繫結已經初始化好的資料(基本型別、陣列、物件例項),還可以用匿名函式來繫結。用匿名函式的好處在於,這個服務繫結到容器以後,並不會立即產生服務最終的物件,只有在這個服務解析的時候,匿名函式才會執行,此時才會產生這個服務對應的服務例項。
實際上,當我們使用singleton,bind方法以及陣列形式,(這三個方法是後面要介紹的繫結的方法),進行服務繫結的時候,如果繫結的服務形式,不是一個匿名函式,也會在laravel內部用一個匿名函式包裝起來,這樣的話, 不輪繫結什麼內容,都能做到前面介紹的懶初始化的功能,這對於容器的效能是有好處的。這個可以從bind的原始碼中看到一些細節:
if (! $concrete instanceof Closure) { $concrete = 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 abstract, $parameters); }/*
- Resolve the given type from the container.
- @param string $abstract
- @param array $parameters
- @return mixed */protected function resolve($abstract, $parameters = []){ $abstract = abstract); parameters) || ! is_null( 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(abstract]) && ! $needsContextualBuild) { return abstract]; } $this->with[] = $parameters; $concrete = 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 (concrete, $abstract)) { $object = concrete); } else { $object = 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 (abstract) as $extender) { $object = object, KaTeX parse error: Expected 'EOF', got '}' at position 8: this); }̲ // If the requ…this->isShared($abstract) && ! $needsContextualBuild) { abstract] = $object; } 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. abstract] = true; array_pop($this->with); return $object; } 第一步:
parameters) || ! is_null( abstract) ); 該方法主要是區分,解析的物件是否有引數,如果有引數,還需要對引數做進一步的分析,因為傳入的引數,也可能是依賴注入的,所以還需要對傳入的引數進行解析;這個後面再分析。
第二步:
if (isset(abstract]) && ! $needsContextualBuild) { return abstract]; } 如果是繫結的單例,並且不需要上面的引數依賴。我們就可以直接返回 abstract]。
第三步:
$concrete = abstract); …/**
- Get the concrete type for a given abstract.
- @param string $abstract
- @return mixed abstract){ if (! is_null($concrete = abstract))) { return KaTeX parse error: Expected 'EOF', got '}' at position 11: concrete; }̲ // If we don't…this->bindings[$abstract])) { return 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 (concrete, $abstract)) { $object = concrete); } else { $object = 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 是個閉包,則執行 concrete),否則,表示存在巢狀依賴的情況,則採用遞迴的方法執行 concrete),直到所有的都解析完為止。
concrete)
/**
- Instantiate a concrete instance of the given type.
- @param string $concrete
- @return mixed
- @throws IlluminateContractsContainerBindingResolutionException */public function build(KaTeX parse error: Expected '}', got 'EOF' at end of input: …行閉包函式,返回結果 if (concrete instanceof Closure) { return