成人怡红院-成人怡红院视频在线观看-成人影视大全-成人影院203nnxyz-美女毛片在线看-美女免费黄

站長(zhǎng)資訊網(wǎng)
最全最豐富的資訊網(wǎng)站

Laravel實(shí)例詳解之容器、控制反轉(zhuǎn)和依賴注入

本篇文章給大家?guī)砹岁P(guān)于Laravel的相關(guān)知識(shí),其中主要介紹了關(guān)于容器、控制反轉(zhuǎn)以及依賴注入的相關(guān)問題,下面就一起來看一下什么相關(guān)的內(nèi)容,希望對(duì)大家有幫助。

Laravel實(shí)例詳解之容器、控制反轉(zhuǎn)和依賴注入

推薦學(xué)習(xí):Laravel入門

隨著現(xiàn)在應(yīng)用的規(guī)模越來越龐大,對(duì)象之間的依賴關(guān)系也越來越復(fù)雜,耦合程度越來越高,經(jīng)常會(huì)出現(xiàn)對(duì)象之間多重依賴的情況。對(duì)于如此龐大復(fù)雜的應(yīng)用,任何修改都可能會(huì)牽一發(fā)而動(dòng)全身,這就為應(yīng)用的后期維護(hù)造成了很多困擾。

??為了解決對(duì)象之間耦合度高的問題,控制反轉(zhuǎn)(IoC)的思想也隨之誕生。所謂控制反轉(zhuǎn),是面向?qū)ο缶幊讨械囊环N設(shè)計(jì)原則,其目的是為了降低代碼之間的耦合程度。在 Laravel 中,控制反轉(zhuǎn)是通過依賴注入(DI)的方式實(shí)現(xiàn)的。

??控制反轉(zhuǎn)的基本思想是借助 IoC 容器實(shí)現(xiàn)對(duì)象之間的依賴關(guān)系的解耦。引入 IoC 容器之后,所有對(duì)象的控制權(quán)都上交給 IoC 容器,IoC 容器成了整個(gè)系統(tǒng)的核心,把所有對(duì)象粘合在一起發(fā)揮作用。Laravel 中的容器即起到了這個(gè)作用。

⒈ 容器

??所謂容器,在 Laravel 中指的是 IlluminateFoundationApplication 對(duì)象,Laravel 框架在啟動(dòng)時(shí)即創(chuàng)建了該對(duì)象。

# public/index.php $app = require_once __DIR__.'/../bootstrap/app.php'; # bootstrap/app.php $app = new IlluminateFoundationApplication(     $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__) );

??在創(chuàng)建容器的過程中,Laravel 還會(huì)對(duì)容器進(jìn)行一些基礎(chǔ)的綁定和服務(wù)注冊(cè)。Laravel 首先會(huì)將容器實(shí)例與 app 和 IlluminateContainerContainer 進(jìn)行綁定;之后,Laravel 會(huì)將基礎(chǔ)的服務(wù)提供者注冊(cè)到容器實(shí)例中,包括事件、日志、路由服務(wù)提供者;最后,Laravel 會(huì)將框架核心 class 與其相對(duì)應(yīng)的別名一起注冊(cè)到容器實(shí)例當(dāng)中。

// namespace IlluminateFoundationApplication public function __construct($basePath = null) {     if ($basePath) {         $this->setBasePath($basePath);     }     $this->registerBaseBindings();     $this->registerBaseServiceProviders();     $this->registerCoreContainerAliases(); }      protected function registerBaseBindings() {     static::setInstance($this);     $this->instance('app', $this);     $this->instance(Container::class, $this);     /* ... ... */ } protected function registerBaseServiceProviders() {     $this->register(new EventServiceProvider($this));     $this->register(new LogServiceProvider($this));     $this->register(new RoutingServiceProvider($this)); } public function registerCoreContainerAliases() {     foreach ([         'app' => [self::class, IlluminateContractsContainerContainer::class, IlluminateContractsFoundationApplication::class, PsrContainerContainerInterface::class],         /* ... ...*/         'db' => [IlluminateDatabaseDatabaseManager::class, IlluminateDatabaseConnectionResolverInterface::class],         'db.connection' => [IlluminateDatabaseConnection::class, IlluminateDatabaseConnectionInterface::class],         /* ... ... */         'request' => [IlluminateHttpRequest::class, SymfonyComponentHttpFoundationRequest::class],         'router' => [IlluminateRoutingRouter::class, IlluminateContractsRoutingRegistrar::class, IlluminateContractsRoutingBindingRegistrar::class],         /* ... ... */     ] as $key => $aliases) {         foreach ($aliases as $alias) {             $this->alias($key, $alias);         }     } } // namespace IlluminateContainerContainer public function alias($abstract, $alias) {     if ($alias === $abstract) {         throw new LogicException("[{$abstract}] is aliased to itself.");     }     $this->aliases[$alias] = $abstract;     $this->abstractAliases[$abstract][] = $alias; }

??在完成這三步基本的注冊(cè)之后,我們可以很方便的訪問已經(jīng)注冊(cè)到容器中的對(duì)象實(shí)例。例如,可以直接通過 $app['app'] 或 $app['IlluminateContainerContainer'] 訪問容器本身,還可以通過 $app['db'] 直接訪問數(shù)據(jù)庫連接。

⒉ 服務(wù)提供者的注冊(cè)以及服務(wù)的訪問

注冊(cè)服務(wù)提供者

??在容器創(chuàng)建的過程中會(huì)注冊(cè)基礎(chǔ)服務(wù)提供者,其注冊(cè)過程通過調(diào)用 register() 方法完成。

// namespace IlluminateFoundationApplication public function register($provider, $force = false) {     if (($registered = $this->getProvider($provider)) && ! $force) {         return $registered;     }     if (is_string($provider)) {         $provider = $this->resolveProvider($provider);     }     $provider->register();     if (property_exists($provider, 'bindings')) {         foreach ($provider->bindings as $key => $value) {             $this->bind($key, $value);         }     }     if (property_exists($provider, 'singletons')) {         foreach ($provider->singletons as $key => $value) {             $this->singleton($key, $value);         }     }     $this->markAsRegistered($provider);     if ($this->isBooted()) {         $this->bootProvider($provider);     }     return $provider; }

??Laravel 首先會(huì)判斷指定的服務(wù)提供者是否已經(jīng)在容器中注冊(cè)(通過調(diào)用 getProvider() 方法實(shí)現(xiàn)),如果指定的服務(wù)提供者已經(jīng)在容器中注冊(cè),并且本次注冊(cè)操作并非強(qiáng)制執(zhí)行,那么直接返回已經(jīng)注冊(cè)好的服務(wù)提供者。

??如果不滿足上述條件,那么 Laravel 就會(huì)開始注冊(cè)服務(wù)提供者。此時(shí),如果傳參為字符串,那么 Laravel 會(huì)默認(rèn)參數(shù)為服務(wù)提供者的 class 名稱并進(jìn)行實(shí)例化(通過 resolveProvider() 方法實(shí)現(xiàn))。之后,就會(huì)調(diào)用服務(wù)提供者定義的 register() 方法進(jìn)行注冊(cè)。以日志服務(wù)提供者為例,其 register() 方法的方法體如下

// namespace IlluminateLogLogServiceProvider public function register() {     $this->app->singleton('log', function ($app) {         return new LogManager($app);     }); }

??register() 方法的作用就是將 IlluminateLogLogManager 對(duì)象以單例的模式注冊(cè)到容器當(dāng)中,注冊(cè)完成之后,容器的 $bindings 屬性中會(huì)增加一項(xiàng)

$app->bindings['log'] = [     'concrete' => 'IlluminateLogLogManager {#162}',     'shared' => true, ];

??如果服務(wù)提供者自身還定義了 $bindings 屬性以及 $singletons 屬性,那么 Laravel 還會(huì)調(diào)用相應(yīng)的 bind() 方法和 singleton() 方法完成這些服務(wù)提供者自定義的綁定的注冊(cè)。

??這之后 Laravel 會(huì)將服務(wù)提供者標(biāo)記為已經(jīng)注冊(cè)的狀態(tài),隨后會(huì)調(diào)用服務(wù)提供者定義的 boot() 方法啟動(dòng)服務(wù)提供者(前提是應(yīng)用已經(jīng)啟動(dòng))。

??在向容器中注冊(cè)綁定時(shí),有 bind() 和 singleton() 兩種方法,其區(qū)別僅在于注冊(cè)的綁定是否為單例模式,即 shared 屬性是否為 true 。

// namespace IlluminateContainerContainer public function singleton($abstract, $concrete = null) {     $this->bind($abstract, $concrete, true); } public function bind($abstract, $concrete = null, $shared = false) {     // 刪除舊的綁定     $this->dropStaleInstances($abstract);     if (is_null($concrete)) {         $concrete = $abstract;     }     if (! $concrete instanceof Closure) {         if (! is_string($concrete)) {             throw new TypeError(self::class.'::bind(): Argument #2 ($concrete) must be of type Closure|string|null');         }         $concrete = $this->getClosure($abstract, $concrete);     }     $this->bindings[$abstract] = compact('concrete', 'shared');     if ($this->resolved($abstract)) {         $this->rebound($abstract);     } } protected function getClosure($abstract, $concrete) {     return function ($container, $parameters = []) use ($abstract, $concrete) {         if ($abstract == $concrete) {             return $container->build($concrete);         }         return $container->resolve(             $concrete, $parameters, $raiseEvents = false         );     }; }

??仍然以日志服務(wù)提供者為例,日志服務(wù)提供者在注冊(cè)時(shí)以單例模式進(jìn)行注冊(cè),并且 $concrete 參數(shù)為閉包。在綁定開始之前,Laravel 首先會(huì)刪除舊的綁定。由于此時(shí) $concrete 為閉包,所以 Laravel 并不會(huì)進(jìn)行什么操作,只是將綁定信息存入 $bindings 屬性當(dāng)中。

訪問服務(wù)

??在服務(wù)提供者注冊(cè)完成之后,我們可以用上文提到的類似訪問數(shù)據(jù)庫連接的方式那樣訪問服務(wù)。仍然以日志服務(wù)為例,我們可以通過 $app['log'] 的方式訪問日志服務(wù)。另外,在 Laravel 中,我們還可以使用 facade 的方式訪問服務(wù),例如,我們可以調(diào)用 IlluminateSupportFacadesLog::info() 來記錄日志。

// namespace IlluminateSupportFacadesLog class Log extends Facade {     protected static function getFacadeAccessor()     {         return 'log';     } } // namespace IlluminateSupportFacadesFacade public static function __callStatic($method, $args) {     $instance = static::getFacadeRoot();     /* ... ... */     return $instance->$method(...$args); } public static function getFacadeRoot() {     return static::resolveFacadeInstance(static::getFacadeAccessor()); } protected static function resolveFacadeInstance($name) {     if (is_object($name)) {         return $name;     }     if (isset(static::$resolvedInstance[$name])) {         return static::$resolvedInstance[$name];     }     if (static::$app) {         return static::$resolvedInstance[$name] = static::$app[$name];     } }

??在通過靜態(tài)調(diào)用的方式進(jìn)行日志記錄時(shí),首先會(huì)訪問 Facade 中的魔術(shù)方法 __callStatic() ,該方法的首先進(jìn)行的就是解析出 facade 對(duì)應(yīng)的服務(wù)實(shí)例,然后調(diào)用該服務(wù)實(shí)例下的方法來執(zhí)行相應(yīng)的功能。每個(gè) facade 中都會(huì)定義一個(gè) getFacadeAccessor() 方法,這個(gè)方法會(huì)返回一個(gè) tag,在日志服務(wù)中,這個(gè) tag 就是日志服務(wù)提供者的閉包在容器的 $bindings 屬性中的 key。也就是說,通過 facade 方式最終得到的是 $app['log']。

??那么為什么可以通過關(guān)聯(lián)數(shù)組的方式訪問容器中注冊(cè)的對(duì)象/服務(wù)?IlluminateContainerContainer 實(shí)現(xiàn)了 ArrayAccess 并且定義了 OffsetGet() 方法,而 IlluminateFoundationApplication 繼承了 Container ,$app 為 Application 實(shí)例化的對(duì)象,所以通過關(guān)聯(lián)數(shù)組的方式訪問容器中注冊(cè)的對(duì)象時(shí)會(huì)訪問 Container 的 OffsetGet() 方法。在 OffsetGet() 方法中會(huì)調(diào)用 Container 的 make() 方法,而 make() 方法中又會(huì)調(diào)用 resolve() 方法。resolve() 方法最終會(huì)解析并返回相應(yīng)的對(duì)象。

// namespace IlluminateContainer public function offsetGet($key) {     return $this->make($key); } public function make($abstract, array $parameters = []) {     return $this->resolve($abstract, $parameters); } protected function resolve($abstract, $parameters = [], $raiseEvents = true) {     /* ... ... */     $this->with[] = $parameters;     if (is_null($concrete)) {         $concrete = $this->getConcrete($abstract);     }     if ($this->isBuildable($concrete, $abstract)) {         $object = $this->build($concrete);     } else {         $object = $this->make($concrete);     }     /* ... ... */     $this->resolved[$abstract] = true;     array_pop($this->with);     return $object; } protected function getConcrete($abstract) {     if (isset($this->bindings[$abstract])) {         return $this->bindings[$abstract]['concrete'];     }     return $abstract; } protected function isBuildable($concrete, $abstract) {     return $concrete === $abstract || $concrete instanceof Closure; } public function build($concrete) {     if ($concrete instanceof Closure) {         return $concrete($this, $this->getLastParameterOverride());     }     /* ... ... */ } protected function getLastParameterOverride() {     return count($this->with) ? end($this->with) : []; }

??這里需要說明,在通過 $app['log'] 的方式解析日志服務(wù)實(shí)例時(shí),resolve() 方法中的 $concrete 解析得到的是一個(gè)閉包,導(dǎo)致 isBuildable() 方法返回結(jié)果為 true,所以 Laravel 會(huì)直接調(diào)用 build() 方法。而由于此時(shí) $concrete 是一個(gè)閉包,所以在 build() 方法中會(huì)直接執(zhí)行這個(gè)閉包函數(shù),最終返回 LogManager 實(shí)例。

⒊ 請(qǐng)求處理

??在基礎(chǔ)的綁定和服務(wù)注冊(cè)完成之后,容器創(chuàng)建成功并返回 $app 。之后 Laravel 會(huì)將內(nèi)核(包括 Http 內(nèi)核和 Console 內(nèi)核)和異常處理注冊(cè)到容器當(dāng)中。然后 Laravel 開始處理請(qǐng)求。

// namespace bootstrap/app.php $app->singleton(     IlluminateContractsHttpKernel::class,     AppHttpKernel::class ); $app->singleton(     IlluminateContractsConsoleKernel::class,     AppConsoleKernel::class ); $app->singleton(     IlluminateContractsDebugExceptionHandler::class,     AppExceptionsHandler::class ); // public/index.php $kernel = $app->make(IlluminateContractsHttpKernel::class); $response = $kernel->handle(     $request = Request::capture() )->send(); $kernel->terminate($request, $response);

??在開始處理請(qǐng)求之前,Laravel 首先會(huì)解析出 Http 內(nèi)核對(duì)象 $kernel,即 AppHttpKernel 實(shí)例化的對(duì)象。而 AppHttpKernel 繼承了 IlluminateFoundationKernel,所以 $kernel 實(shí)際調(diào)用的是 IlluminateFoundationKernel 中的 handle() 方法。

namespace IlluminateFoundationHttp use IlluminateContractsDebugExceptionHandler public function handle($request) {     try {         $request->enableHttpMethodParameterOverride();         $response = $this->sendRequestThroughRouter($request);     } catch (Throwable $e) {         $this->reportException($e);         $response = $this->renderException($request, $e);     }     $this->app['events']->dispatch(         new RequestHandled($request, $response)     );     return $response; } // 上報(bào)錯(cuò)誤 protected function reportException(Throwable $e) {     $this->app[ExceptionHandler::class]->report($e); } // 渲染錯(cuò)誤信息     protected function renderException($request, Throwable $e) {     return $this->app[ExceptionHandler::class]->render($request, $e); }

??handle() 方法在處理請(qǐng)求的過程中如果出現(xiàn)任何異常或錯(cuò)誤,Laravel 都會(huì)調(diào)用容器中已經(jīng)注冊(cè)好的異常處理對(duì)象來上報(bào)異常并且渲染返回信息。

??在容器創(chuàng)建成功以后,Laravel 會(huì)將 IlluminateContractsDebugExceptionHandler 和 AppExceptionsHandler 之間的綁定注冊(cè)到容器當(dāng)中,所以 Laravel 處理異常實(shí)際調(diào)用的都是 AppExceptionsHandler 中的方法。在實(shí)際開發(fā)過程中,開發(fā)者可以根據(jù)自身需要在 AppExceptionsHandler 中自定義 report() 和 render() 方法。

在 PHP 7 中,`Exception` 和 `Error` 是兩種不同的類型,但它們同時(shí)都繼承了 `Throwable` ,所以 `handler()` 方法中捕獲的是 `Throwable` 對(duì)象。

??在正式開始處理請(qǐng)求之前,Laravel 會(huì)進(jìn)行一些引導(dǎo)啟動(dòng),包括加載環(huán)境變量、配置信息等,這些引導(dǎo)啟動(dòng)在 Laravel 運(yùn)行過程中起到了非常重要的作用。

// namespace IlluminateFoundationHttpKernel protected $bootstrappers = [     IlluminateFoundationBootstrapLoadEnvironmentVariables::class,     IlluminateFoundationBootstrapLoadConfiguration::class,     IlluminateFoundationBootstrapHandleExceptions::class,     IlluminateFoundationBootstrapRegisterFacades::class,     IlluminateFoundationBootstrapRegisterProviders::class,     IlluminateFoundationBootstrapBootProviders::class, ]; protected function sendRequestThroughRouter($request) {     /* ... ... */     $this->bootstrap();     /* ... ... */ } public function bootstrap() {     if (! $this->app->hasBeenBootstrapped()) {         $this->app->bootstrapWith($this->bootstrappers());     } } // namespace IlluminateFoundationApplication public function bootstrapWith(array $bootstrappers) {     $this->hasBeenBootstrapped = true;     foreach ($bootstrappers as $bootstrapper) {         $this['events']->dispatch('bootstrapping: '.$bootstrapper, [$this]);         $this->make($bootstrapper)->bootstrap($this);         $this['events']->dispatch('bootstrapped: '.$bootstrapper, [$this]);     } }

??從代碼中可以看出,引導(dǎo)啟動(dòng)的過程實(shí)際就是調(diào)用各個(gè) class 中的 bootstrap() 方法。其中:

LoadEnvironmentVariables 用來加載環(huán)境變量

LoadConfiguration 用來加載 config 目錄下的配置文件

HandleExceptions 用來設(shè)置 PHP 的錯(cuò)誤報(bào)告級(jí)別以及相應(yīng)的異常和錯(cuò)誤處理函數(shù),另外還會(huì)設(shè)置 PHP 的程序終止執(zhí)行函數(shù)

// namespace IlluminateFoundationBootstrapHandleExceptions public function bootstrap(Application $app) {     /* ... ... */     $this->app = $app;     error_reporting(-1);     set_error_handler([$this, 'handleError']);     set_exception_handler([$this, 'handleException']);     register_shutdown_function([$this, 'handleShutdown']);     /* ... ... */ } public function handleError($level, $message, $file = '', $line = 0, $context = []) {     if (error_reporting() & $level) {         /* ... ... */         throw new ErrorException($message, 0, $level, $file, $line);     } } public function handleException(Throwable $e) {     /* ... ... */         $this->getExceptionHandler()->report($e);     /* ... ... */ } public function handleShutdown() {     if (! is_null($error = error_get_last()) && $this->isFatal($error['type'])) {         $this->handleException($this->fatalErrorFromPhpError($error, 0));     } } protected function getExceptionHandler() {     return $this->app->make(IlluminateContractsDebugExceptionHandler::class); }

??從以上代碼中可以看出,雖然 HandleExceptions 中定義了異常、錯(cuò)誤、程序終止的處理函數(shù),但無論是哪種情況,最終還是調(diào)用 AppExceptionsHandler 中的方法來處理異常或錯(cuò)誤。

RegisterFacades 的作用一個(gè)是注冊(cè)配置文件以及第三方包中自定義的 alias 類,還有一個(gè)非常重要的作用就是為 IlluminateSupportFacadesFacade 類設(shè)置 $app 屬性。

// namespace IlluminateFoundationBootstrapRegisterFAcades public function bootstrap(Application $app) {     Facade::clearResolvedInstances();     Facade::setFacadeApplication($app);     AliasLoader::getInstance(array_merge(         $app->make('config')->get('app.aliases', []),         $app->make(PackageManifest::class)->aliases()     ))->register(); }

&emsp?我們?cè)谕ㄟ^ facade 方式反問容器中注冊(cè)的服務(wù)時(shí),F(xiàn)acade 在解析容器中的服務(wù)實(shí)例時(shí)用到的 static::$app 即是在這個(gè)時(shí)候設(shè)置的。

RegisterProviders 的作用是注冊(cè)配置文件以及第三方包中定義的服務(wù)提供者

// namespace IlluminateFoundationBootstrapRegisterProviders public function bootstrap(Application $app) {     $app->registerConfiguredProviders(); } public function registerConfiguredProviders() {     $providers = Collection::make($this->make('config')->get('app.providers'))                     ->partition(function ($provider) {                         return strpos($provider, 'Illuminate\') === 0;                     });     $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);     (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))                 ->load($providers->collapse()->toArray()); }

??在實(shí)際注冊(cè)的過程中,Laravel 會(huì)按照 Laravel 框架的服務(wù)提供者 > 第三方包的服務(wù)提供者 > 開發(fā)者自定義的服務(wù)提供者 的順序進(jìn)行注冊(cè)

BootProviders 則是按順序調(diào)用已經(jīng)注冊(cè)到容器中的服務(wù)提供者的 boot() 方法(前提是服務(wù)提供者定義的 boot() 方法)

??在引導(dǎo)啟動(dòng)完成之后,Laravel 開始處理請(qǐng)求,首先要做的就是將全局的中間件應(yīng)用于 request 。這之后 Laravel 會(huì)將請(qǐng)求分發(fā)到相應(yīng)的路由進(jìn)行處理,處理之前需要先根據(jù) request 找到相應(yīng)的路由對(duì)象 IlluminateRoutingRoute。在 Laravel 中,除了全局中間件,還有一些中間件只作用于特定的路由或路由分組,此時(shí)這些中間件就會(huì)被作用于 request 。這些工作都完成之后,路由對(duì)象開始執(zhí)行代碼,完成請(qǐng)求。

// namespace IlluminateFoundationHttpKernel protected function sendRequestThroughRouter($request) {     /* ... ... */     return (new Pipeline($this->app))                 ->send($request)                 ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)                 ->then($this->dispatchToRouter()); } protected function dispatchToRouter() {     return function ($request) {         $this->app->instance('request', $request);                  return $this->router->dispatch($request);     }; } // namespace IlluminateRoutingRouter public function dispatch(Request $request) {     $this->currentRequest = $request;     return $this->dispatchToRoute($request); } public function dispatchToRoute(Request $request) {     return $this->runRoute($request, $this->findRoute($request)); } protected function runRoute(Request $request, Route $route) {     /* ... ... */     return $this->prepareResponse($request,         $this->runRouteWithinStack($route, $request)     ); } protected function runRouteWithinStack(Route $route, Request $request) {     /* ... ... */        return (new Pipeline($this->container))                     ->send($request)                     ->through($middleware)                     ->then(function ($request) use ($route) {                         return $this->prepareResponse(                             $request, $route->run()                         );                     }); }

⒋ 依賴注入

??Laravel 中的路由在注冊(cè)時(shí),action 可以是控制器方法,也可以是閉包。但無論是那種形式,都需要傳參,而傳參就會(huì)遇到需要依賴注入的情況。

??Route 對(duì)象在執(zhí)行 run() 方法時(shí)會(huì)根據(jù) action 的類型分別進(jìn)行控制器方法調(diào)用或閉包函數(shù)的調(diào)用。但兩種方法最終都需要解析參數(shù),而如果參數(shù)中用到了 class ,就需要進(jìn)行依賴注入。

// namespace IlluminateRoutingRouter public function run() {     $this->container = $this->container ?: new Container;     try {         if ($this->isControllerAction()) {             return $this->runController();         }         return $this->runCallable();     } catch (HttpResponseException $e) {         return $e->getResponse();     } } protected function runController() {     return $this->controllerDispatcher()->dispatch(         $this, $this->getController(), $this->getControllerMethod()     ); } protected function runCallable() {     /* ... ... */     return $callable(...array_values($this->resolveMethodDependencies(         $this->parametersWithoutNulls(), new ReflectionFunction($callable)     ))); } // namespace IlluminateRoutingControllerDispatcher public function dispatch(Route $route, $controller, $method) {     $parameters = $this->resolveClassMethodDependencies(         $route->parametersWithoutNulls(), $controller, $method     );     /* ... ... */ } // namespace IlluminateRoutingRouteDependencyResolverTrait protected function resolveClassMethodDependencies(array $parameters, $instance, $method) {     /* ... ... */     return $this->resolveMethodDependencies(         $parameters, new ReflectionMethod($instance, $method)     ); } public function resolveMethodDependencies(array $parameters, ReflectionFunctionAbstract $reflector) {     /* ... ... */     foreach ($reflector->getParameters() as $key => $parameter) {         $instance = $this->transformDependency($parameter, $parameters, $skippableValue);         /* ... ... */     }     return $parameters; } protected function transformDependency(ReflectionParameter $parameter, $parameters, $skippableValue) {     $className = Reflector::getParameterClassName($parameter);     if ($className && ! $this->alreadyInParameters($className, $parameters)) {         return $parameter->isDefaultValueAvailable() ? null : $this->container->make($className);     }     return $skippableValue; }

??在執(zhí)行過程中,Laravel 首先通過反射取得參數(shù)列表(對(duì)于控制器方法,使用 ReflectionMethod ,對(duì)于閉包函數(shù),則使用 ReflectionFunction )。在得到參數(shù)列表后,Laravel 仍然是利用反射,逐個(gè)判斷參數(shù)類型。如果參數(shù)類型為 PHP 的內(nèi)置類型,那么不需要什么特殊處理;但如果參數(shù)不是 PHP 內(nèi)置類型,則需要利用反射解析出參數(shù)的具體類型。在解析出參數(shù)的具體類型之后,緊接著會(huì)判斷該類型的對(duì)象是不是已經(jīng)存在于參數(shù)列表中,如果不存在并且該類型也沒有設(shè)置默認(rèn)值,那么就需要通過容器創(chuàng)建出該類型的實(shí)例。

??要通過容器創(chuàng)建指定 class 的實(shí)例,仍然需要用到 resolve() 方法。前文已經(jīng)敘述過使用 resolve() 方法解析閉包函數(shù)的情況,所以這里值敘述實(shí)例化 class 的情況。

// namespace IlluminateContainerContainer public function build($concrete) {     /* ... ... */     try {         $reflector = new ReflectionClass($concrete);     } catch (ReflectionException $e) {         throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e);     }     if (! $reflector->isInstantiable()) {         return $this->notInstantiable($concrete);     }     $this->buildStack[] = $concrete;     $constructor = $reflector->getConstructor();     if (is_null($constructor)) {         array_pop($this->buildStack);         return new $concrete;     }     $dependencies = $constructor->getParameters();     try {         $instances = $this->resolveDependencies($dependencies);     } catch (BindingResolutionException $e) {         array_pop($this->buildStack);         throw $e;     }     array_pop($this->buildStack);     return $reflector->newInstanceArgs($instances); } protected function resolveDependencies(array $dependencies) {     $results = [];     foreach ($dependencies as $dependency) {         if ($this->hasParameterOverride($dependency)) {             $results[] = $this->getParameterOverride($dependency);             continue;         }         $result = is_null(Util::getParameterClassName($dependency))                         ? $this->resolvePrimitive($dependency)                         : $this->resolveClass($dependency);         if ($dependency->isVariadic()) {             $results = array_merge($results, $result);         } else {             $results[] = $result;         }     }     return $results; }

??容器在實(shí)例化 class 的時(shí)候,仍然是通過反射獲取 class 基本信息。對(duì)于一些無法進(jìn)行實(shí)例化的 class (例如 interface 、abstract class ),Laravel 會(huì)拋出異常;否則 Laravel 會(huì)繼續(xù)獲取 class 的構(gòu)造函數(shù)的信息。對(duì)于不存在構(gòu)造函數(shù)的 class ,意味著這些 class 在實(shí)例化的時(shí)候不需要額外的依賴,可以直接通過 new 來實(shí)例化;否則仍然是通過反射解析出構(gòu)造函數(shù)的參數(shù)列表信息,然后逐個(gè)實(shí)例化這些參數(shù)列表中用到的 class 。在這些參數(shù)列表中的 class 都實(shí)例化完成之后,通過容器創(chuàng)建 class 的準(zhǔn)備工作也已經(jīng)完成,此時(shí)容器可以順利創(chuàng)建出指定 class 的實(shí)例,然后注入到控制器方法或閉包中。

推薦學(xué)習(xí):Laravel入門

贊(0)
分享到: 更多 (0)
?
網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)
厨房挺人ⅩXXX猛交| 国产99在线 | 亚洲| 抱着边走边撞水流了一地的水| А√天堂资源地址在线官网BT| 成人永久免费福利视频免费| 国产成人精品综合久久久久| 国产网红在线_电影频道| 精品国产V无码大片在线看| 久久精品一区二区三区AV| 美女与野兽在线观看| 欧美一性一交一免费看| 色欲av成人蜜臀av不卡| 羞涩的丰满人妻40P| 野花香高清在线观看视频播放免费| 制服丝袜AV无码专区完整版| WWW久久久天天COM| 国产成网站18禁止久久影院| 狠狠色噜噜狠狠狠8888米奇| 毛豆日产精品卡2卡3卡4卡免费 | 国产AV无码专区亚洲AⅤ| 国色天香在线视频免费观看| 久久人妻少妇偷人精品综合桃色| 欧美高清熟妇啪啪内射不卡自拍| 少妇私密会所按摩到高潮呻吟| 亚洲AV无码XXX麻豆艾秋| 又大又粗又爽A级毛片免费看| ワンピースのエロ官网| 国产精品毛片VA一区二区三区| 精品人妻少妇一级毛片免费| 欧美乱大交XXXXX| 吸咬奶头狂揉60分钟视频| 在线观看片免费人成视频无码 | 日产精品乱码卡一卡2卡三网站| 无人区码一码二码三码是| 一本久久伊人热热精品中文| 成人免费一区二区三区视频| 黑人巨大两根一起挤进| 欧美无人区码卡二三卡四卡| 午夜福利1000集无码| 中文字幕一区二区人妻5566| 国产成人精品一区二三区| 久久久久亚洲AV成人片一区| 色偷偷人人澡久久超碰97| 亚洲精品无码7777| 超碰色偷偷男人的天堂| 精品少妇无码AV无码专区蜜臀| 人妻夜夜添夜夜无码AV| 亚洲精品成人福利网站| 被群CAO的合不拢腿H纯肉视频| 狠狠躁夜夜躁人人躁婷婷 | 无码人妻一区二区三区免水牛视频| 再灬再灬再灬深一点舒服| 国产成人亚洲精品无码AV大片| 麻豆日产精品卡2卡3卡4卡5卡| 我调教同学的放荡麻麻| 18亚洲AV无码成人网站国产| 国产午夜精华无码网站| 人妻少妇无码精品视频区| 亚洲色偷偷综合亚洲AV伊人蜜桃| 俄罗斯人和欧洲人的性格差异| 久久精品国内一区二区三区| 天堂M和天堂2M区别| 50岁退休熟女露脸高潮| 韩国三级香港三级日本三级L | 麻豆精品一区二正一三区| 我们还没在书房试过| HEYZO高清中文字幕在线| 花色98堂新网名内容与理念| 日日噜噜夜夜狠狠视频无码 | 亚洲成A人V在线蜜臀| 成人亚洲A片Ⅴ一区二区小说| 看黄A大片爽爽影院免费无码| 无码人妻啪啪一区二区| 八戒八戒在线WWW观看| 久久蜜桃86人妻arvi| 学渣含着学霸几巴的写作业视频| 八戒八戒神马2021| 免费精品一区二区三区第35| 亚洲国产精品久久久天堂不卡海量| 俄罗斯卖CSGO的网站免费进入| 内地丰满浓密老熟女露脸对白| 亚洲国产AⅤ精品一区二区蜜桃| 丰满岳乱妇在线观看中字无码| 欧美XXXXX高潮喷水麻豆| 亚洲熟妇一区二区| 国产真实乱对白精彩久久| 色婷婷亚洲一区二区三区 | 国产精品秘 入口A级熟女| 人妻系列无码专区无码专区| 性欧美亚洲XXXX乳在线观看| 久久18禁高潮出水呻吟娇喘| 亚洲 欧美 激情 另类 校园| 高潮喷奶水在线播放视频| 欧美人与动牲交A精品| 在办公室把护士给爽了动态图| 婚后失控BY南城非梦| 无码人妻精品一区二区三区9厂| 草莓视频在线播放视频| 欧美多人片高潮野外做片黑人| 一个吃我奶头两个舔我下面| 回民丰满少妇XXX性| 亚洲AⅤ秘区二区三区4| 国产成人久久精品二区三区| 色老久久精品偷偷鲁| 宝贝小嫩嫩好紧好爽H在线视频| 欧美丰满美乳XXⅩ高潮| 中文在线っと好きだっ最新版 | AV无码中出一区二区三区| 猫咪AV成人永久网站| 一二三四在线视频社区8| 久久精品国产一区二区电影 | 天天摸日日摸狠狠添| 粉嫩AV一区二区夜夜嗨| 日本久久三级电影院| H纯肉无码精品动漫在线观看| 免费毛片45分钟| 中文无码精品A∨在线观看不卡| 久久久久亚洲AV无码网站| 亚洲香蕉成人AAAV在线网站| 精品人无码一区二区三区| 亚洲另类春色国产精品| 精品久久久久久无码国产| 亚洲国产美女久久久久| 好硬好大好爽18禁免费看男男| 亚洲AV素人乱码| 好爽好紧好大的免费视频国产| 亚洲AV无码乱码精品国产| 黑人玩弄漂亮少妇高潮大叫| 亚洲妇女水蜜桃AV网网站| 精品久久久久久无码人妻| 亚洲人妻无码一区二区| 久久久国产精品ⅤA麻豆| 伊人久久大香线蕉AV色婷婷色| 老湿机香蕉久久久久久| 中文字幕夫の上司に犯新沢平兰| 麻豆国产AV丝袜白领传媒| 最新国产在线拍揄自揄视频| 女刑警被两个黑人挺进| WWW.一区二区三区在线 || 人妻无码ΑV中文字幕久久琪琪布| 暴躁少女CSGO图片| 少妇被黑人到高潮喷出白浆| 国产精品18久久久久久麻辣 | 日韩精品无码一本二本三本色 | AV无码不卡一区二区三区| 日本理论片YY4800免费| 丰满少妇AAAAAA爰片毛片| 五十路六十路七十路熟婆| 激情射精爆插热吻无码视频| 亚洲中文字幕无码久久2020| 美日韩AV成人影院| WWW亚洲精品自慰一区二区| 色哟哟网站在线观看入口| 国产精品一区二区 尿失禁| 亚洲国产精品一区二区第一页| 久久综合激的五月天的歌词| AⅤ中文字幕不卡在线无码| 日本三级强乳伦姧| 国产精品久久久久7777按摩| 亚洲国产精品VA在线观看香蕉| 浪潮AV色综合久久天堂| BBW下身丰满18XXXX| 熟妇人妻AV中文字幕老熟妇| 国色天香在线视频免费观看| 伊人久久大香线蕉AV色| 欧美性巨大╳╳╳╳╳高跟鞋| 父母儿女一家换着玩的文案| 亚洲AV无码成人专区| 麻豆文化传媒精品| 成人无码特黄特黄AV片在线| 性色ΑV一二三天美传媒| 久久综合给合久久狠狠狠97色6 | 三上悠亚AV影院在线看| 国产一区二区无码蜜芽精品| 一碰就有水的女人男人喜欢?| 人妻被黑人与白人巨大中出| 国产成人无码AV一区二区 | 老师你乖乖的可以让你少吃点苦头| 爱情岛网站亚洲禁18进入 | 2018最新国产好看的国产| 十八禁啪啪污污网站免费下载| 护士交换粗吟配乱大交| 97人妻人人揉人人躁人人| 玩弄丰满熟妇XXXXX性60| 久久久AV波多野一区二区| 波多野42部无码喷潮在线| 亚洲AV色香蕉一区二区蜜桃| 蜜臀av一区二区蜜臀AV免费| 国产★蜜臀AV无码8MAV| 亚洲性XXXXX极品少妇| 日本丰满人妻HD浓毛| 黄 色 网 站 在 线 免费| JAPANXXXXHDAV无码| 亚洲AV无码成人精品网站漏男| 年轻老师的滋味5| 国产裸体XXXX视频在线播放| 中文字幕一本性无码| 午夜性又黄又爽免费看尤物| 男人J放进女人J无遮挡免费看| 国产精品未满十八禁止观看|