zoukankan      html  css  js  c++  java
  • Lumen开发:lumen源码解读之初始化(2)——门面(Facades)与数据库(db)

    版权声明:本文为博主原创文章,未经博主允许不得转载。

    紧接上一篇

    $app->withFacades();//为应用程序注册门面。
     
    $app->withEloquent();//为应用程序加载功能强大的库。
    

    先来看看withFacades()

    /**
         * Register the facades for the application.(为应用程序注册门面。)
         *
         * @param  bool  $aliases
         * @param  array $userAliases
         * @return void
         */
        public function withFacades($aliases = true, $userAliases = [])
        {
            Facade::setFacadeApplication($this);
    
            if ($aliases) {
                $this->withAliases($userAliases);
            }
        }
    
    setFacadeApplication()
        /**
         * Set the application instance.(设置应用程序实例。)
         *
         * @param  IlluminateContractsFoundationApplication  $app
         * @return void
         */
        public static function setFacadeApplication($app)
        {
            static::$app = $app;
        }
    

    将当前实例传给门面类(Facade)

    $this->withAliases($userAliases)

        /**
         * Register the aliases for the application.(注册应用程序的别名。)
         *
         * @param  array  $userAliases
         * @return void
         */
        public function withAliases($userAliases = [])
        {
            $defaults = [
                'IlluminateSupportFacadesAuth' => 'Auth',
                'IlluminateSupportFacadesCache' => 'Cache',
                'IlluminateSupportFacadesDB' => 'DB',
                'IlluminateSupportFacadesEvent' => 'Event',
                'IlluminateSupportFacadesGate' => 'Gate',
                'IlluminateSupportFacadesLog' => 'Log',
                'IlluminateSupportFacadesQueue' => 'Queue',
                'IlluminateSupportFacadesSchema' => 'Schema',
                'IlluminateSupportFacadesURL' => 'URL',
                'IlluminateSupportFacadesValidator' => 'Validator',
            ];
    
            if (! static::$aliasesRegistered) {//判断是否已注册类别名。
                static::$aliasesRegistered = true;
    
                $merged = array_merge($defaults, $userAliases);
    
                foreach ($merged as $original => $alias) {
                    class_alias($original, $alias);//设置别名
                }
            }
        }
    

    然后就是withEloquent()函数

        /**
         * Load the Eloquent library for the application.(为应用程序加载功能强大的库。)
         *
         * @return void
         */
        public function withEloquent()
        {
            $this->make('db');
        }
    

      

        /**
         * Resolve the given type from the container.(从容器中解析给定类型。)
         *
         * @param  string  $abstract
         * @return mixed
         */
        public function make($abstract)
        {
            $abstract = $this->getAlias($abstract);
    
            if (array_key_exists($abstract, $this->availableBindings) &&
                ! array_key_exists($this->availableBindings[$abstract], $this->ranServiceBinders)) {
                $this->{$method = $this->availableBindings[$abstract]}();
    
                $this->ranServiceBinders[$method] = true;
            }
    
            return parent::make($abstract);
        }
    

    一步一步来,make()函数以后经常用得上。

        /**
         * Get the alias for an abstract if available.(如果可用的话,获取抽象的别名。)
         *
         * @param  string  $abstract
         * @return string
         *
         * @throws LogicException
         */
        public function getAlias($abstract)
        {
            if (! isset($this->aliases[$abstract])) {  //$this->aliases是注册类别名,如果没有别名,则直接返回$abstract
                return $abstract;
            }
    
            if ($this->aliases[$abstract] === $abstract) {  //如果$abstract是别名本身,则会抛出异常
                throw new LogicException("[{$abstract}] is aliased to itself.");
            }
    
            return $this->getAlias($this->aliases[$abstract]);
        }
    

    这一个的$this->aliases的值来着上一篇文章registerContainerAliases()函数对它的赋值

    接下来是这段

        if (array_key_exists($abstract, $this->availableBindings) &&
                ! array_key_exists($this->availableBindings[$abstract], $this->ranServiceBinders)) {
                $this->{$method = $this->availableBindings[$abstract]}();
    
                $this->ranServiceBinders[$method] = true;
            }
    
    $this->availableBindings变量在Applilcation类的定义是是
        /**
         * The available container bindings and their respective load methods.(可用的容器绑定及其各自的加载方法。)
         *
         * @var array
         */
        public $availableBindings = [
            'auth' => 'registerAuthBindings',
            'auth.driver' => 'registerAuthBindings',
            'IlluminateAuthAuthManager' => 'registerAuthBindings',
            'IlluminateContractsAuthGuard' => 'registerAuthBindings',
            'IlluminateContractsAuthAccessGate' => 'registerAuthBindings',
            'IlluminateContractsBroadcastingBroadcaster' => 'registerBroadcastingBindings',
            'IlluminateContractsBroadcastingFactory' => 'registerBroadcastingBindings',
            'IlluminateContractsBusDispatcher' => 'registerBusBindings',
            'cache' => 'registerCacheBindings',
            'cache.store' => 'registerCacheBindings',
            'IlluminateContractsCacheFactory' => 'registerCacheBindings',
            'IlluminateContractsCacheRepository' => 'registerCacheBindings',
            'composer' => 'registerComposerBindings',
            'config' => 'registerConfigBindings',
            'db' => 'registerDatabaseBindings',
            'IlluminateDatabaseEloquentFactory' => 'registerDatabaseBindings',
            'encrypter' => 'registerEncrypterBindings',
            'IlluminateContractsEncryptionEncrypter' => 'registerEncrypterBindings',
            'events' => 'registerEventBindings',
            'IlluminateContractsEventsDispatcher' => 'registerEventBindings',
            'files' => 'registerFilesBindings',
            'hash' => 'registerHashBindings',
            'IlluminateContractsHashingHasher' => 'registerHashBindings',
            'log' => 'registerLogBindings',
            'PsrLogLoggerInterface' => 'registerLogBindings',
            'queue' => 'registerQueueBindings',
            'queue.connection' => 'registerQueueBindings',
            'IlluminateContractsQueueFactory' => 'registerQueueBindings',
            'IlluminateContractsQueueQueue' => 'registerQueueBindings',
            'PsrHttpMessageServerRequestInterface' => 'registerPsrRequestBindings',
            'PsrHttpMessageResponseInterface' => 'registerPsrResponseBindings',
            'translator' => 'registerTranslationBindings',
            'url' => 'registerUrlGeneratorBindings',
            'validator' => 'registerValidatorBindings',
            'IlluminateContractsValidationFactory' => 'registerValidatorBindings',
            'view' => 'registerViewBindings',
            'IlluminateContractsViewFactory' => 'registerViewBindings',
        ];
    

    $this->ranServiceBinders变量是记录已执行的服务绑定方法。
    当前执行的是make('db'),所以$abstract=‘db’;
    $this->availableBindings['db'] = 'registerDatabaseBindings';
    $this->{$method = $this->availableBindings[$abstract]}();
    

    会执行到Application的registerDatabaseBindings方法

        /**
         * Register container bindings for the application.(为应用程序注册容器绑定。)
         *
         * @return void
         */
        protected function registerDatabaseBindings()
        {
            $this->singleton('db', function () {  //这里是用闭包函数注册一个db的单例
                return $this->loadComponent(
                    'database', [
                        'IlluminateDatabaseDatabaseServiceProvider',
                        'IlluminatePaginationPaginationServiceProvider',
                    ], 'db'
                );
            });
        }
    
    这里是用闭包函数注册一个db的单例,接着看闭包内执行了什么
        /**
         * Configure and load the given component and provider.(配置并加载给定的组件和提供程序。)
         *
         * @param  string  $config
         * @param  array|string  $providers
         * @param  string|null  $return
         * @return mixed
         */
        public function loadComponent($config, $providers, $return = null)
        {
            $this->configure($config);//将配置文件加载到应用程序中。
    
            foreach ((array) $providers as $provider) {
                $this->register($provider);//注册传过来的服务供应类
            }
    
            return $this->make($return ?: $config);//
        }
    
    因为这里第一次初始化后,$this->ranServiceBinders[$method]=true,所以以后调用db时,都会直接调用父类(Container)的make()函数。
    讲远了的感觉,不过刚好可以一起讲一下最后一步return parent::make($abstract);
        /**
         * Resolve the given type from the container.
         *
         * @param  string  $abstract
         * @return mixed
         */
        public function make($abstract)
        {
            return $this->resolve($abstract);
        }
    

    Container类的make()只调用了$this->resolve()函数,马不停蹄,我们来看看这个函数

    /**
         * 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 (isset($this->instances[$abstract]) && ! $needsContextualBuild) {  //单例已存在直接返回
                return $this->instances[$abstract];
            }
    
            $this->with[] = $parameters;
    
            $concrete = $this->getConcrete($abstract);  //返回给定抽象的具体类型(包括一些关于上下文的绑定操作)
    
            //我们已经准备好实例化绑定注册的具体类型的实例。
            //这将实例化类型,以及递归地解析所有的“嵌套”依赖关系,直到所有问题都得到解决为止。
            if ($this->isBuildable($concrete, $abstract)) {  //判断是否为递归
                $object = $this->build($concrete);  //递归用build(),用make()会死循环
            } else {
                $object = $this->make($concrete);   //非递归用make()
            }
    
            //如果我们定义了这种类型的扩展程序,
            //我们需要将它们旋转并将它们应用到正在构建的对象中。
            //这允许扩展服务,例如更改配置或装饰对象。
            foreach ($this->getExtenders($abstract) as $extender) {  //执行扩展
                $object = $extender($object, $this);
            }
    
            //如果请求的类型被注册为单例,我们将缓存“内存”中的实例,
            //以便稍后返回它,而不必为每一个后续请求创建一个对象的全新实例。
            if ($this->isShared($abstract) && ! $needsContextualBuild) {
                $this->instances[$abstract] = $object;
            }
    
            $this->fireResolvingCallbacks($abstract, $object);  //回调
    
            //返回之前,我们还将解析的标志设置为“true”,
            //并弹出此构建的参数重写。完成这两件事后,我们将准备返回完全构造的类实例。
            $this->resolved[$abstract] = true;
    
            array_pop($this->with);  //移除最后一个键值,也就是$this->with[] = $parameters;
         return $object; 
    }

    这里是整篇文比较难的地方,部分注释是直译的,讲了这么久,其实也执行了$app->withFacades()和$app->withEloquent();

    其实后面的一些注册和绑定与前面也是类似的,希望能帮到大家!

    Lumen技术交流群:310493206

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    选择排序遇到的引用和传值问题记录
    The web application [ROOT] appears to have started a thread named [spring.cloud.inetutils] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
    IDEA中实用的插件
    Column 'status' specified twice
    Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'dataType' in 'field list'
    You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where id = 2' at line 8
    Missing URI template variable 'id' for method parameter of type long
    值传递和引用传递的区别
    SpringBoot项目与数据库交互,访问http://localhost:8888/admin/userInfo时,报org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0
    SpringBoot项目启动时报错:org.apache.catalina.LifecycleException: Protocol handler start failed
  • 原文地址:https://www.cnblogs.com/cxscode/p/7568589.html
Copyright © 2011-2022 走看看