Laravel 依賴注入源碼解析

在 Laravel 的控制器的構造方法或者成員方法,均可以經過類型約束的方式使用依賴注入,如:php

public function store(Request $request)
{
    //TODO
}

這裏 $request 參數就使用了類型約束,Request 是類型約束的類型,它是一個類:IlluminateHttpRequest.laravel

本文研究 Laravel 的依賴注入原理,爲何這樣定義不須要實例化就能夠直接使用 Request 的方法呢?只是框架幫咱們實例化並傳參了,咱們看看這個過程。框架

1.路由定義

從源頭開始看起,在路由定義文件中定義了這麼一個路由:this

Route::resource('/role', 'Admin\RoleController');

這是一個資源型的路由,Laravel 會自動生成增刪改查的路由入口。
file
本文開頭的 store 方法就是一個控制器的方法,圖中可見路由定義的 Action 也是:AppHttpControllersAdminRoleController@storespa

路由方法解析

根據路由定義找到控制器和方法,這個過程在 dispatch 方法中實現。code

(文件:vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php)orm

public function dispatch(Route $route, $controller, $method)
{
    $parameters = $this->resolveClassMethodDependencies(
        $route->parametersWithoutNulls(), $controller, $method
    );
 
    if (method_exists($controller, 'callAction')) {
        return $controller->callAction($method, $parameters);
    }
 
    return $controller->{$method}(...array_values($parameters));
}

這裏 resolveClassMethodDependencies 方法,「顧名思義」這個方法的做用是從類的方法中獲取依賴對象:對象

protected function resolveClassMethodDependencies(array $parameters, $instance, $method)
{
    if (! method_exists($instance, $method)) {
        return $parameters;
    }
 
    return $this->resolveMethodDependencies(
        $parameters, new ReflectionMethod($instance, $method)
    );
}

這裏重點就是用到了 PHP 的反射,注意 RelectionMethod 方法,它獲取到類的方法參數列表,能夠知道參數的類型約束,參數名稱等等。blog

這裏的 $instance 參數就是 RoleController 控制器類,$method 參數就是方法名稱 strore.ip

2.獲取依賴對象的示例

從方法的參數中獲取了依賴對象的約束類型,就能夠實例化這個依賴的對象。

protected function transformDependency(ReflectionParameter $parameter, $parameters)
{
    $class = $parameter->getClass();
 
    // If the parameter has a type-hinted class, we will check to see if it is already in
    // the list of parameters. If it is we will just skip it as it is probably a model
    // binding and we do not want to mess with those; otherwise, we resolve it here.
    if ($class && ! $this->alreadyInParameters($class->name, $parameters)) {
        return $parameter->isDefaultValueAvailable()
            ? $parameter->getDefaultValue()
            : $this->container->make($class->name);
    }
}

根據類名從容器中獲取對象,這個綁定對象實例的過程在服務提供者中先定義和了。

而後把實例化的對象傳入到 store 方法中,就能夠使用依賴的對象了。

3.關於 PHP 反射

舉個使用 ReflectionMethod 的例子。

class Demo
{
    private $request;
 
    public function store(Request $request)
    {
 
    }
}

打印出 new ReflectionMethod(Demo::class, ‘store’) 的內容如圖:
file

能夠得出這個方法的參數列表,參數的約束類型,如 typeHint,IlluminateHttpRequest.

根據類名能夠從容器中獲取一開始經過服務提供者綁定的實例。

(原文地址:https://blog.tanteng.me/2018/...

相關文章
相關標籤/搜索