【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
我们知道Facade门面模式是一个中介类,对子系统或者类对象进行封装和代理,为子系统的一组接口提供一个统一的高层接口。它对外暴露子系统的功能,但是不暴露子系统对象,用户要使用子系统的某个功能(方法)时,先调用门店,由于门店代理了子系统,所以委托子系统去处理相应的功能请求,从而达到将用户和子系统解耦的目的,用户只需要和门店打交道,不需要知道所有的子系统及其内部构造。
我们接下来通过在Laravel中最常用的DB-Facade,来看看Facade在Laravel中是如何工作的。
1,代码如下
<?php
use Exception;
use Illuminate\Support\Facades\DB;
Class A {
function a() {
try {
DB::beginTransaction();
//do something
DB::commit();
} catch (Exception $e) {
DB::rollback();
// handle exception
}
}
}
这里,我们看到use了一个DB的Facades,为什么写了DB两个字母,就会自动use DB的Facade呢?这就涉及到 Laravel的自动加载和依赖注入机制,这里略过。
我们来看看这个类文件的代码:
2, DB Facades
<?php
namespace Illuminate\Support\Facades;
/**
* @method static \Illuminate\Database\ConnectionInterface connection(string $name = null)
* @method static string getDefaultConnection()
* @method static void setDefaultConnection(string $name)
* @method static \Illuminate\Database\Query\Builder table(string $table)
* @method static \Illuminate\Database\Query\Expression raw($value)
* @method static mixed selectOne(string $query, array $bindings = [])
* @method static array select(string $query, array $bindings = [])
* @method static bool insert(string $query, array $bindings = [])
* @method static int update(string $query, array $bindings = [])
* @method static int delete(string $query, array $bindings = [])
* @method static bool statement(string $query, array $bindings = [])
* @method static int affectingStatement(string $query, array $bindings = [])
* @method static bool unprepared(string $query)
* @method static array prepareBindings(array $bindings)
* @method static mixed transaction(\Closure $callback, int $attempts = 1)
* @method static void beginTransaction()
* @method static void commit()
* @method static void rollBack()
* @method static int transactionLevel()
* @method static array pretend(\Closure $callback)
*
* @see \Illuminate\Database\DatabaseManager
* @see \Illuminate\Database\Connection
*/
class DB extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'db';
}
}
发现它只有一个getFacadeAccessor静态方法,返回一个'db'的字符串,其他方法都没有。
那它到底是怎么执行的其他方法呢,比如这里的beginTranscation()方法?我们知道,当调用一个不存在的静态方法时,PHP会自动调用__callStatic()魔术方法,而这里没有,那我们只能从它继承的Facade父类中看看。
3,Facade父类 / 基类
<?php
namespace Illuminate\Support\Facades;
use Closure;
use Mockery;
use RuntimeException;
use Mockery\MockInterface;
abstract class Facade
{
/**
* The application instance being facaded.
*
* @var \Illuminate\Contracts\Foundation\Application
*/
protected static $app;
/**
* The resolved object instances.
*
* @var array
*/
protected static $resolvedInstance;
/**
* Run a Closure when the facade has been resolved.
*
* @param \Closure $callback
* @return void
*/
public static function resolved(Closure $callback)
{
static::$app->afterResolving(static::getFacadeAccessor(), function ($service) use ($callback) {
$callback($service);
});
}
/**
* Convert the facade into a Mockery spy.
*
* @return \Mockery\MockInterface
*/
public static function spy()
{
if (! static::isMock()) {
$class = static::getMockableClass();
return tap($class ? Mockery::spy($class) : Mockery::spy(), function ($spy) {
static::swap($spy);
});
}
}
/**
* Initiate a mock expectation on the facade.
*
* @return \Mockery\Expectation
*/
public static function shouldReceive()
{
$name = static::getFacadeAccessor();
$mock = static::isMock()
? static::$resolvedInstance[$name]
: static::createFreshMockInstance();
return $mock->shouldReceive(...func_get_args());
}
/**
* Create a fresh mock instance for the given class.
*
* @return \Mockery\Expectation
*/
protected static function createFreshMockInstance()
{
return tap(static::createMock(), function ($mock) {
static::swap($mock);
$mock->shouldAllowMockingProtectedMethods();
});
}
/**
* Create a fresh mock instance for the given class.
*
* @return \Mockery\MockInterface
*/
protected static function createMock()
{
$class = static::getMockableClass();
return $class ? Mockery::mock($class) : Mockery::mock();
}
/**
* Determines whether a mock is set as the instance of the facade.
*
* @return bool
*/
protected static function isMock()
{
$name = static::getFacadeAccessor();
return isset(static::$resolvedInstance[$name]) &&
static::$resolvedInstance[$name] instanceof MockInterface;
}
/**
* Get the mockable class for the bound instance.
*
* @return string|null
*/
protected static function getMockableClass()
{
if ($root = static::getFacadeRoot()) {
return get_class($root);
}
}
/**
* Hotswap the underlying instance behind the facade.
*
* @param mixed $instance
* @return void
*/
public static function swap($instance)
{
static::$resolvedInstance[static::getFacadeAccessor()] = $instance;
if (isset(static::$app)) {
static::$app->instance(static::getFacadeAccessor(), $instance);
}
}
/**
* Get the root object behind the facade.
*
* @return mixed
*/
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
/**
* Get the registered name of the component.
*
* @return string
*
* @throws \RuntimeException
*/
protected static function getFacadeAccessor()
{
throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
}
/**
* Resolve the facade root instance from the container.
*
* @param string|object $name
* @return mixed
*/
protected static function resolveFacadeInstance($name)
{
if (is_object($name)) {
return $name;
}
if (isset(static::$resolvedInstance[$name])) {
return static::$resolvedInstance[$name];
}
return static::$resolvedInstance[$name] = static::$app[$name];
}
/**
* Clear a resolved facade instance.
*
* @param string $name
* @return void
*/
public static function clearResolvedInstance($name)
{
unset(static::$resolvedInstance[$name]);
}
/**
* Clear all of the resolved instances.
*
* @return void
*/
public static function clearResolvedInstances()
{
static::$resolvedInstance = [];
}
/**
* Get the application instance behind the facade.
*
* @return \Illuminate\Contracts\Foundation\Application
*/
public static function getFacadeApplication()
{
return static::$app;
}
/**
* Set the application instance.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public static function setFacadeApplication($app)
{
static::$app = $app;
}
/**
* Handle dynamic, static calls to the object.
*
* @param string $method
* @param array $args
* @return mixed
*
* @throws \RuntimeException
*/
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
if (! $instance) {
throw new RuntimeException('A facade root has not been set.');
}
return $instance->$method(...$args);
}
}
这里的基类是给所有Facade子类继承的,很明显,是不可能有子类的静态方法的。所以只能看__callStatic()方法,也就是最后这个。
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
if (! $instance) {
throw new RuntimeException('A facade root has not been set.');
}
return $instance->$method(...$args);
}
它的第一步,是获取在Facade后面的 根对象 (root object)。
/**
* Get the root object behind the facade.
*
* @return mixed
*/
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
这里的 static::getFacadeAccessor()方法是不是很眼熟,对,它就是DB-Facade类里面唯一的一个方法,返回字符串'db'。OK,我们看它的主方法resolveFacadeInstance,代码如下。它会从容器中解析 facade 根实例。
/**
* Resolve the facade root instance from the container.
*
* @param string|object $name
* @return mixed
*/
protected static function resolveFacadeInstance($name)
{
if (is_object($name)) {
return $name;
}
if (isset(static::$resolvedInstance[$name])) {
return static::$resolvedInstance[$name];
}
return static::$resolvedInstance[$name] = static::$app[$name];
}
如果是第一次解析,它就会从 static::$app这个数组中,去获取key 为 字符串 'db' 的值。
那我们看看 static::$app是什么。
/**
* The application instance being facaded.
*
* @var \Illuminate\Contracts\Foundation\Application
*/
protected static $app;
是正在被门店代理的应用实例,来自于 \Illuminate\Contracts\Foundation\Application.
这是一个契约,有契约就会有实现契约的类,全局搜一下整个框架,发现,唯一的实现,是在 namespace Illuminate\Foundation\Application 下 (vendor/laravel/framework/src/Illuminate/Foundation/Application.php),我们来看看
<?php
namespace Illuminate\Foundation;
.
.
use Symfony\Component\HttpKernel\HttpKernelInterface;
.
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
class Application extends Container implements ApplicationContract, HttpKernelInterface
{
/**
* Create a new Illuminate application instance.
*
* @param string|null $basePath
* @return void
*/
public function __construct($basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}
$this->registerBaseBindings();
$this->registerBaseServiceProviders();
$this->registerCoreContainerAliases();
}
}
代码太多,就截取了最关键的部分。可以看到这个类实现了上面的 ApplicationContract。
看到构造方法,其实很熟悉了,是在将各种基本的绑定,基本的服务提供者,还有核心容器别名,注入到 服务容器中,也就是 $app中。
也就是说,我们要在服务容器$app中,找一个名为 'db' 的 facade 根实例。
2,$app
我们知道,在 Laravel 的入口文件 index.php文件中,第一步是注册自动加载器,第二步实例化出服务容器 $app,第三步是将内核启动,处理请求。
其中在处理请求中,有一个步骤是 bootstrap(),为HTTP请求运行起应用。
/**
* Bootstrap the application for HTTP requests.
*
* @return void
*/
public function bootstrap()
{
if (! $this->app->hasBeenBootstrapped()) {
$this->app->bootstrapWith($this->bootstrappers());
}
}
这里会让服务容器 $app 将 很多需要启动的组件给启动起来,如下:
/**
* The bootstrap classes for the application.
*
* @var array
*/
protected $bootstrappers = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
而 $this->app->bootstrapWith() 是怎么处理这些 bootstrap 类呢?
这里的bootstrapWith() 方法,点进去,它是在 vendor/laravel/framework/src/Illuminate/Contracts/Foundation/Application.php 文件中,
namespace Illuminate\Contracts\Foundation;
use Closure;
use Illuminate\Contracts\Container\Container;
interface Application extends Container
{
/**
* Run the given array of bootstrap classes.
*
* @param array $bootstrappers
* @return void
*/
public function bootstrapWith(array $bootstrappers);
}
又是一个接口,那谁实现的呢?就是上面提到的vendor/laravel/framework/src/Illuminate/Foundation/Application.php,代码如下
/**
* Run the given array of bootstrap classes.
*
* @param string[] $bootstrappers
* @return void
*/
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]);
}
}
这里传入一个数组,然后实例化他们,获取到对象后,分别调用了各自的bootstrap方法。
我们看两个:RegisterFacades 和 RegisterProviders。
2.1 RegisterFacades 的 bootstrap()
文件: vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/RegisterFacades.php
<?php
namespace Illuminate\Foundation\Bootstrap;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\Facades\Facade;
use Illuminate\Foundation\PackageManifest;
use Illuminate\Contracts\Foundation\Application;
class RegisterFacades
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
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();
}
}
它关键的一步,是将config/app.php文件中的aliases这些 类的别名 和 具体的 Facades,加上PackageManifest包列表的别名,一起打包, 将这些映射关系 调用 register() 方法,进行注册。注意以下的注释部分,这些别名是被 懒加载 处理的,也就是说,等到程序要用的时候才会真正被加载进来,所以不会影响性能。
/*
|--------------------------------------------------------------------------
| Class Aliases
|--------------------------------------------------------------------------
|
| This array of class aliases will be registered when this application
| is started. However, feel free to register as many as you wish as
| the aliases are "lazy" loaded so they don't hinder performance.
|
*/
'aliases' => [
'App' => Illuminate\Support\Facades\App::class,
.
'DB' => Illuminate\Support\Facades\DB::class,
],
这是RegisterFacades部分。我们再看 RegisterProviders。
2.2 RegisterProviders
目录: vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/RegisterProviders.php
<?php
namespace Illuminate\Foundation\Bootstrap;
use Illuminate\Contracts\Foundation\Application;
class RegisterProviders
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
$app->registerConfiguredProviders();
}
}
这里的registerConfiguredProviders()方法点进去,又是一个接口:
目录: vendor/laravel/framework/src/Illuminate/Contracts/Foundation/Application.php
<?php
namespace Illuminate\Contracts\Foundation;
use Illuminate\Contracts\Container\Container;
interface Application extends Container
{
/**
* Register all of the configured providers.
*
* @return void
*/
public function registerConfiguredProviders();
}
具体的实现,还是在 vendor/laravel/framework/src/Illuminate/Foundation/Application.php
<?php
namespace Illuminate\Foundation;
use Illuminate\Support\ServiceProvider;
use Illuminate\Events\EventServiceProvider;
use Illuminate\Routing\RoutingServiceProvider;
use Symfony\Component\HttpKernel\HttpKernelInterface;
.
use Illuminate\Contracts\Http\Kernel as HttpKernelContract;
.
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
class Application extends Container implements ApplicationContract, HttpKernelInterface
{
/**
* Register all of the configured providers.
*
* @return void
*/
public function registerConfiguredProviders()
{
$providers = Collection::make($this->config['app.providers'])
->partition(function ($provider) {
return Str::startsWith($provider, 'Illuminate\\');
});
$providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);
(new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
->load($providers->collapse()->toArray());
}
}
它会将config/app.php配置文件的providers, 以 'Illuminate\\' 开头的类 当做第一部分,其他的为第二部分。然后将 packageManifest插入到中间,最后,providers就是这个三个元素的数组,如下:
object(Illuminate\Support\Collection)#32 (1) {
["items":protected]=>
array(3) {
[0]=>
object(Illuminate\Support\Collection)#22 (1) {
["items":protected]=>
array(22) {
[0]=>
string(35) "Illuminate\Auth\AuthServiceProvider"
[1]=>
string(48) "Illuminate\Broadcasting\BroadcastServiceProvider"
[2]=>
string(33) "Illuminate\Bus\BusServiceProvider"
[3]=>
string(37) "Illuminate\Cache\CacheServiceProvider"
[4]=>
string(61) "Illuminate\Foundation\Providers\ConsoleSupportServiceProvider"
[5]=>
string(39) "Illuminate\Cookie\CookieServiceProvider"
[6]=>
string(43) "Illuminate\Database\DatabaseServiceProvider"
[7]=>
string(47) "Illuminate\Encryption\EncryptionServiceProvider"
[8]=>
string(47) "Illuminate\Filesystem\FilesystemServiceProvider"
[9]=>
string(57) "Illuminate\Foundation\Providers\FoundationServiceProvider"
[10]=>
string(38) "Illuminate\Hashing\HashServiceProvider"
[11]=>
string(35) "Illuminate\Mail\MailServiceProvider"
[12]=>
string(52) "Illuminate\Notifications\NotificationServiceProvider"
[13]=>
string(47) "Illuminate\Pagination\PaginationServiceProvider"
[14]=>
string(43) "Illuminate\Pipeline\PipelineServiceProvider"
[15]=>
string(37) "Illuminate\Queue\QueueServiceProvider"
[16]=>
string(37) "Illuminate\Redis\RedisServiceProvider"
[17]=>
string(54) "Illuminate\Auth\Passwords\PasswordResetServiceProvider"
[18]=>
string(41) "Illuminate\Session\SessionServiceProvider"
[19]=>
string(49) "Illuminate\Translation\TranslationServiceProvider"
[20]=>
string(47) "Illuminate\Validation\ValidationServiceProvider"
[21]=>
string(35) "Illuminate\View\ViewServiceProvider"
}
}
[1]=>
array(9) {
[0]=>
string(43) "Fideloper\Proxy\TrustedProxyServiceProvider"
[1]=>
string(37) "Jacobcyl\AliOSS\AliOssServiceProvider"
[2]=>
string(52) "Illuminate\Notifications\NexmoChannelServiceProvider"
[3]=>
string(52) "Illuminate\Notifications\SlackChannelServiceProvider"
[4]=>
string(36) "Laravel\Tinker\TinkerServiceProvider"
[5]=>
string(38) "Maatwebsite\Excel\ExcelServiceProvider"
[6]=>
string(30) "Carbon\Laravel\ServiceProvider"
[7]=>
string(45) "SimpleSoftwareIO\QrCode\QrCodeServiceProvider"
[8]=>
string(43) "Spatie\Permission\PermissionServiceProvider"
}
[2]=>
object(Illuminate\Support\Collection)#30 (1) {
["items":protected]=>
array(8) {
[22]=>
string(32) "App\Providers\AppServiceProvider"
[23]=>
string(33) "App\Providers\AuthServiceProvider"
[24]=>
string(34) "App\Providers\EventServiceProvider"
[25]=>
string(34) "App\Providers\RouteServiceProvider"
[26]=>
string(39) "App\Providers\ApiRequestServiceProvider"
[27]=>
string(37) "Jacobcyl\AliOSS\AliOssServiceProvider"
[28]=>
string(38) "Maatwebsite\Excel\ExcelServiceProvider"
[29]=>
string(33) "App\Providers\TestServiceProvider"
}
}
}
}
然后用 vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php 这个服务提供者仓库(ProviderRepository),将这些服务提供者,逐个load 加载注册 到 $app 中。
<?php
namespace Illuminate\Foundation;
use Exception;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
class ProviderRepository
{
/**
* Register the application service providers.
*
* @param array $providers
* @return void
*/
public function load(array $providers)
{
$manifest = $this->loadManifest();
// First we will load the service manifest, which contains information on all
// service providers registered with the application and which services it
// provides. This is used to know which services are "deferred" loaders.
if ($this->shouldRecompile($manifest, $providers)) {
$manifest = $this->compileManifest($providers);
}
// Next, we will register events to load the providers for each of the events
// that it has requested. This allows the service provider to defer itself
// while still getting automatically loaded when a certain event occurs.
foreach ($manifest['when'] as $provider => $events) {
$this->registerLoadEvents($provider, $events);
}
// We will go ahead and register all of the eagerly loaded providers with the
// application so their services can be registered with the application as
// a provided service. Then we will set the deferred service list on it.
foreach ($manifest['eager'] as $provider) {
$this->app->register($provider);
}
$this->app->addDeferredServices($manifest['deferred']);
}
}
执行顺序如下:
2.2.1 如果存在服务提供者缓存清单,则直接读取「服务提供者」集合;
2.2.2 否则,将从 config/app.php 配置中的服务提供者编译到缓存清单中;
2.2.2.1 这个处理由 compileManifest() 方法完成,之后$manifest是一个数组,含 providers, eager, when, deferred四个字段,如下
array(4) {
["when"]=>
array(14) {
["Illuminate\Broadcasting\BroadcastServiceProvider"]=>
array(0) {
}
["Illuminate\Bus\BusServiceProvider"]=>
array(0) {
}
["Illuminate\Cache\CacheServiceProvider"]=>
array(0) {
}
.
}
["providers"]=>
array(39) {
[0]=>
string(35) "Illuminate\Auth\AuthServiceProvider"
[1]=>
string(48) "Illuminate\Broadcasting\BroadcastServiceProvider"
[2]=>
string(33) "Illuminate\Bus\BusServiceProvider"
[3]=>
string(37) "Illuminate\Cache\CacheServiceProvider"
[4]=>
string(61) "Illuminate\Foundation\Providers\ConsoleSupportServiceProvider"
[5]=>
string(39) "Illuminate\Cookie\CookieServiceProvider"
[6]=>
string(43) "Illuminate\Database\DatabaseServiceProvider"
.
[31]=>
string(32) "App\Providers\AppServiceProvider"
[32]=>
string(33) "App\Providers\AuthServiceProvider"
[33]=>
string(34) "App\Providers\EventServiceProvider"
[34]=>
string(34) "App\Providers\RouteServiceProvider"
[35]=>
string(39) "App\Providers\ApiRequestServiceProvider"
[36]=>
string(37) "Jacobcyl\AliOSS\AliOssServiceProvider"
[37]=>
string(38) "Maatwebsite\Excel\ExcelServiceProvider"
[38]=>
string(33) "App\Providers\TestServiceProvider"
}
["eager"]=>
array(25) {
[0]=>
string(35) "Illuminate\Auth\AuthServiceProvider"
[1]=>
string(39) "Illuminate\Cookie\CookieServiceProvider"
[2]=>
string(43) "Illuminate\Database\DatabaseServiceProvider"
[3]=>
string(47) "Illuminate\Encryption\EncryptionServiceProvider"
[4]=>
string(47) "Illuminate\Filesystem\FilesystemServiceProvider"
[5]=>
string(57) "Illuminate\Foundation\Providers\FoundationServiceProvider"
.
[17]=>
string(32) "App\Providers\AppServiceProvider"
[18]=>
string(33) "App\Providers\AuthServiceProvider"
[19]=>
string(34) "App\Providers\EventServiceProvider"
[20]=>
string(34) "App\Providers\RouteServiceProvider"
[21]=>
string(39) "App\Providers\ApiRequestServiceProvider"
[22]=>
string(37) "Jacobcyl\AliOSS\AliOssServiceProvider"
[23]=>
string(38) "Maatwebsite\Excel\ExcelServiceProvider"
[24]=>
string(33) "App\Providers\TestServiceProvider"
}
["deferred"]=>
array(103) {
["Illuminate\Broadcasting\BroadcastManager"]=>
string(48) "Illuminate\Broadcasting\BroadcastServiceProvider"
["Illuminate\Contracts\Broadcasting\Factory"]=>
string(48) "Illuminate\Broadcasting\BroadcastServiceProvider"
["Illuminate\Contracts\Broadcasting\Broadcaster"]=>
string(48) "Illuminate\Broadcasting\BroadcastServiceProvider"
["Illuminate\Bus\Dispatcher"]=>
string(33) "Illuminate\Bus\BusServiceProvider"
["Illuminate\Contracts\Bus\Dispatcher"]=>
string(33) "Illuminate\Bus\BusServiceProvider"
["Illuminate\Contracts\Bus\QueueingDispatcher"]=>
string(33) "Illuminate\Bus\BusServiceProvider"
["cache"]=>
string(37) "Illuminate\Cache\CacheServiceProvider"
["cache.store"]=>
string(37) "Illuminate\Cache\CacheServiceProvider"
["memcached.connector"]=>
string(37) "Illuminate\Cache\CacheServiceProvider"
["command.cache.clear"]=>
string(61) "Illuminate\Foundation\Providers\ConsoleSupportServiceProvider"
.
string(61) "Illuminate\Foundation\Providers\ConsoleSupportServiceProvider"
["migrator"]=>
.
}
}
我们看到 config/app.php中的providers都被加定义在eager字段下,也就是饥渴加载。
2.2.2.2 编译缓存清单时将处理 贪婪/饥渴加载(eager)和 延迟加载(deferred)的服务提供者;
2.2.3 对于贪婪加载的提供者直接执行服务容器的 register 方法完成服务注册;
2.2.4 将延迟加载提供者加入到服务容器中,按需注册和引导启动。
这边关注需要被饥渴加载的服务提供则,它调用了 register()方法,同样是 vendor/laravel/framework/src/Illuminate/Contracts/Foundation/Application.php 这个 契约接口,仍然是由 Illuminate\Foundation\Application 来实现的,具体如下:
/**
* Register a service provider with the application.
*
* @param \Illuminate\Support\ServiceProvider|string $provider
* @param bool $force
* @return \Illuminate\Support\ServiceProvider
*/
public function register($provider, $force = false)
{
if (($registered = $this->getProvider($provider)) && ! $force) {
return $registered;
}
// If the given "provider" is a string, we will resolve it, passing in the
// application instance automatically for the developer. This is simply
// a more convenient way of specifying your service provider classes.
if (is_string($provider)) {
$provider = $this->resolveProvider($provider);
}
if (method_exists($provider, 'register')) {
$provider->register();
}
// If there are bindings / singletons set as properties on the provider we
// will spin through them and register them with the application, which
// serves as a convenience layer while registering a lot of bindings.
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 the application has already booted, we will call this boot method on
// the provider class so it has an opportunity to do its boot logic and
// will be ready for any usage by this developer's application logic.
if ($this->booted) {
$this->bootProvider($provider);
}
return $provider;
}
关键是这一步,如果provider的控制器中有register方法,则执行该方法。
if (method_exists($provider, 'register')) {
$provider->register();
}
我们在上面的eager字段中包含了 Illuminate\Database\DatabaseServiceProvider 这个服务提供者,让我们来看看它的注册方法。
<?php
namespace Illuminate\Database;
.
.
class DatabaseServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application events.
*
* @return void
*/
public function boot()
{
Model::setConnectionResolver($this->app['db']);
Model::setEventDispatcher($this->app['events']);
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
Model::clearBootedModels();
$this->registerConnectionServices();
$this->registerEloquentFactory();
$this->registerQueueableEntityResolver();
}
/**
* Register the primary database bindings.
*
* @return void
*/
protected function registerConnectionServices()
{
// The connection factory is used to create the actual connection instances on
// the database. We will inject the factory into the manager so that it may
// make the connections while they are actually needed and not of before.
$this->app->singleton('db.factory', function ($app) {
return new ConnectionFactory($app);
});
// The database manager is used to resolve various connections, since multiple
// connections might be managed. It also implements the connection resolver
// interface which may be used by other components requiring connections.
$this->app->singleton('db', function ($app) {
return new DatabaseManager($app, $app['db.factory']);
});
$this->app->bind('db.connection', function ($app) {
return $app['db']->connection();
});
}
.
.
.
}
我们看到它的register()方法调用了registerConnectionServices,而在这个方法里面,我们看到第二步,把 DatabaseManager对象以 'db' 的别名 注册到了 $app 容器中。
百转千回,山重水复,至此,真相终于大白。
原来 Facade 要解析的根对象,就来自于被注册到服务容器$app中的 服务提供者。
/**
* Resolve the facade root instance from the container.
*
* @param string|object $name
* @return mixed
*/
protected static function resolveFacadeInstance($name)
{
if (is_object($name)) {
return $name;
}
if (isset(static::$resolvedInstance[$name])) {
return static::$resolvedInstance[$name];
}
return static::$resolvedInstance[$name] = static::$app[$name];
}
在 调用 DB Facade 中的 beginTransaction() 方法时,实际是由 Illuminate\Database\DatabaseServiceProvider 这个服务提供者 中的 DatabaseManager 去执行的,他们通过 Facade的别名 'db',以及 服务提供者下的DatabaseManager的别名 'db' 进行关联的。
额外说一点,我们在说 将应用启动时,只讲了 RegisterFacades 和 RegisterProviders,其实下面还有一步 BootProviders. 它的步骤和 RegisterProviders差不多,也是通过应用契约,最后让应用去调用每个Provider的方法,只不过RegisterProviders调用的是register()方法,而BootProviders调用的是boot()方法。一个是注册,一个是启动。
/**
* The bootstrap classes for the application.
*
* @var array
*/
protected $bootstrappers = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
至此,我们把Facade在Laravel中的工作机制跑通了一遍,顺带着把服务提供者的注册和启动也过了一遍,让我们对Laravel的请求生命周期也有了更深的了解。
来源:oschina
链接:https://my.oschina.net/u/3412738/blog/3154393