laravel学习之IOC容器分析(一)

在入口文件里可以看到

$app = require_once __DIR__.'/../bootstrap/app.php';

$app这个是laravel的全局变量,在里面可以看到实例化了这个类

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

里面的构造函数做了4件事

1,注册基本的绑定,把当前类的对象绑定到app和Illuminate\Container\Container中

$this->registerBaseBindings();

2,注册服务提供器,里面注册了EventServiceProvider和RoutingServiceProvider,详细的没研究过,以后有机会在研究

$this->registerBaseServiceProviders()

3,注册别名

$this->registerCoreContainerAliases();

4,绑定一些项目内有可能用到的路径

if ($basePath) {
    $this->setBasePath($basePath);
}

 

 

接下来就一个一个的分析一下里面是用来做什么的

第一件事:

protected function registerBaseBindings()
    {
        static::setInstance($this);//获取当前类的实例,规定了要实现ContainerContract接口的类才可以实例化

        $this->instance('app', $this);

        $this->instance('Illuminate\Container\Container', $this);
    }

第一句

public static function setInstance(ContainerContract $container)
    {
        static::$instance = $container;
    }

就把当前类的对象赋值给当前类的$instance变量,方便以后调用

剩下的就是调用instance方法来绑定对象到容器了

看看laravel是怎么绑定对象到容器的

public function instance($abstract, $instance)
    {
        $abstract = $this->normalize($abstract);//如果是字符串,就把前面的斜线去掉
if (is_array($abstract)) { list($abstract, $alias) = $this->extractAlias($abstract); $this->alias($abstract, $alias); }//如果是数组,例如$this->instance(['app'=>'a'],$this),就存进$this->aliases中,变成$this->aliases['app'] = 'a';没用过这个绑定方法,查看其他资料看到的 unset($this->aliases[$abstract]); $bound = $this->bound($abstract);//返回一个布尔值,用于下面判断是否执行回调函数 $this->instances[$abstract] = $instance;//这里就是把变量绑定到instance数组中,例如传进来的是($app,$this),所以绑定成$this->instance['app'] = new a();a代表传进来的类 if ($bound) {//如果为真,就调用$this->reboundCallback[]里有没有回调函数,有的话就调用 $this->rebound($abstract); } }

就是这样绑定了两个变量到容器中了

第二件事:

protected function registerBaseServiceProviders()
    {
        $this->register(new EventServiceProvider($this));

        $this->register(new RoutingServiceProvider($this));
    }

注册了两个服务提供器,一个是事件服务提供器,一个是路由服务提供器

先看看register方法是做什么的 

public function register($provider, $options = [], $force = false)
    {
        if (($registered = $this->getProvider($provider)) && ! $force) {
            return $registered;
        }
if (is_string($provider)) { $provider = $this->resolveProviderClass($provider); } $provider->register();//注册provider
foreach ($options as $key => $value) { $this[$key] = $value; } $this->markAsRegistered($provider);//记录provider为已注册
if ($this->booted) { $this->bootProvider($provider); } return $provider; }

首先判断里面有没有曾经注册过,有的话直接返回

分析一下getProvider()方法

public function getProvider($provider)
    {
        $name = is_string($provider) ? $provider : get_class($provider);

        return Arr::first($this->serviceProviders, function ($key, $value) use ($name) {
            return $value instanceof $name;
        });
    }
public static function first($array, callable $callback = null, $default = null)
{
    if (is_null($callback)) {
        return empty($array) ? value($default) : reset($array);
    }

    foreach ($array as $key => $value) {
        if (call_user_func($callback, $key, $value)) {
            return $value;
        }
    }

    return value($default);
}

这里Arr::first主要是循环$this->serviceProviders这个数组,在里面找到已经注册过的提供器,即如果$this->serviceProviders的键值是当前服务提供器的实例,就返回一个布尔值,然后register方法里面返回当前服务提供器的实例,找不到的话执行下面的代码

 

if (is_string($provider)) {
            $provider = $this->resolveProviderClass($provider);
        }
$provider->register();//注册provider

如果是字符串,直接实例化,然后调用这个类重写之后的register()方法

$this->markAsRegistered($provider);//记录provider为已注册

里面调用了刚注册了的event服务提供器里的fire方法,以后有机会在研究,然后就把当前类记录为已注册,就是把当前服务提供器的类名放进loadedProviders数组里

$this->serviceProviders[] = $provider;
$this->loadedProviders[$class] = true;

然后就调用提供器里的boot方法

if ($this->booted) {
            $this->bootProvider($provider);
        }

里面用了call方法来解决boot里面的依赖问题,laravel学习之IOC容器分析(二)在这里面解释了,然后这样就把一个服务提供器注册成功了。

剩下的第三件事和第四件事就是把别名数组和laravel里面要用到的路径注入到容器中。

到了这里,$app这个全局变量就完成了最基本的工作。

完....

posted @ 2017-05-13 15:20  Gikkson  阅读(1413)  评论(0编辑  收藏  举报